Pocket

Cache LibraryLightweightIn-MemoryHigh SpeedEmbeddedLocal Cache

Cache Library

Pocket

Overview

Pocket is a lightweight and simple in-memory cache library designed as an embedded local cache that provides fast data access without external dependencies. Optimized for small to medium-scale applications and temporary data storage within a single process, it can be utilized immediately with minimal configuration.

Details

Pocket is a cache library that operates directly within applications without requiring complex configurations or external servers. With a design focused on memory efficiency and execution speed, it's suitable for data retention between HTTP requests and temporary storage of computation results.

Key technical features:

  • Lightweight design: Minimal memory footprint
  • Fast access: Low latency through direct memory access
  • Simple API: Intuitive interface with low learning curve
  • TTL support: Time-based automatic expiration
  • Embedded type: No external servers or complex configuration required
  • In-process: Data sharing within a single application

Technical architecture:

  • Hash table: O(1) fast key access
  • Memory management: Automatic garbage collection support
  • Thread safety: Synchronization control as needed
  • Eviction: LRU or size-based deletion policies

Pros and Cons

Pros

  • Immediate availability: Ready to use immediately after installation
  • Lightweight: Minimal resource consumption
  • High speed: Maximum speed through direct memory access
  • Simple: No complex configuration or management required
  • No dependencies: No external servers or services required
  • Easy integration: Simple integration into existing applications
  • Easy debugging: Direct verification of internal state possible

Cons

  • Process limited: Only available within a single process
  • No persistence: Data lost when process terminates
  • No distribution: Cannot share across multiple servers
  • Limited features: Advanced cache features may not be provided
  • Scalability: Unsuitable for large-scale data
  • Memory constraints: Subject to physical memory limitations

Reference Links

Usage Examples

Basic Cache Usage

// Typical Pocket-style implementation example in JavaScript/Node.js
class PocketCache {
    constructor(options = {}) {
        this.cache = new Map();
        this.ttl = options.ttl || 3600000; // Default 1 hour
        this.maxSize = options.maxSize || 1000;
    }
    
    set(key, value, ttl = this.ttl) {
        // Size limit check
        if (this.cache.size >= this.maxSize) {
            this.evictOldest();
        }
        
        const expiresAt = Date.now() + ttl;
        this.cache.set(key, {
            value: value,
            expiresAt: expiresAt,
            accessedAt: Date.now()
        });
    }
    
    get(key) {
        const item = this.cache.get(key);
        if (!item) return null;
        
        // Expiration check
        if (Date.now() > item.expiresAt) {
            this.cache.delete(key);
            return null;
        }
        
        // Update access time (for LRU)
        item.accessedAt = Date.now();
        return item.value;
    }
    
    has(key) {
        return this.get(key) !== null;
    }
    
    delete(key) {
        return this.cache.delete(key);
    }
    
    clear() {
        this.cache.clear();
    }
    
    size() {
        return this.cache.size;
    }
    
    evictOldest() {
        let oldestKey = null;
        let oldestTime = Date.now();
        
        for (const [key, item] of this.cache) {
            if (item.accessedAt < oldestTime) {
                oldestTime = item.accessedAt;
                oldestKey = key;
            }
        }
        
        if (oldestKey) {
            this.cache.delete(oldestKey);
        }
    }
}

// Usage example
const cache = new PocketCache({ maxSize: 100, ttl: 300000 }); // 5 minute TTL

cache.set('user:1000', { name: 'John Doe', email: '[email protected]' });
cache.set('product:500', { name: 'Laptop', price: 999 });

const user = cache.get('user:1000');
console.log('User:', user.name);

Python Implementation Example

import time
import threading
from collections import OrderedDict

class PocketCache:
    def __init__(self, max_size=1000, default_ttl=3600):
        self.cache = OrderedDict()
        self.max_size = max_size
        self.default_ttl = default_ttl
        self.lock = threading.RLock()
    
    def set(self, key, value, ttl=None):
        ttl = ttl or self.default_ttl
        expires_at = time.time() + ttl
        
        with self.lock:
            # Size limit processing
            if len(self.cache) >= self.max_size:
                self.cache.popitem(last=False)  # FIFO removal
            
            self.cache[key] = {
                'value': value,
                'expires_at': expires_at,
                'created_at': time.time()
            }
    
    def get(self, key):
        with self.lock:
            if key not in self.cache:
                return None
            
            item = self.cache[key]
            
            # Expiration check
            if time.time() > item['expires_at']:
                del self.cache[key]
                return None
            
            # LRU: Update access order
            self.cache.move_to_end(key)
            return item['value']
    
    def has(self, key):
        return self.get(key) is not None
    
    def delete(self, key):
        with self.lock:
            return self.cache.pop(key, None) is not None
    
    def clear(self):
        with self.lock:
            self.cache.clear()
    
    def size(self):
        return len(self.cache)
    
    def cleanup_expired(self):
        """Clean up expired items"""
        current_time = time.time()
        with self.lock:
            expired_keys = [
                key for key, item in self.cache.items() 
                if current_time > item['expires_at']
            ]
            for key in expired_keys:
                del self.cache[key]
        return len(expired_keys)

# Usage example
cache = PocketCache(max_size=500, default_ttl=600)  # 10 minute TTL

# Store data
cache.set('session:abc123', {
    'user_id': 1000,
    'username': 'john_doe',
    'login_time': time.time()
})

cache.set('api_response:users', [
    {'id': 1, 'name': 'Alice'},
    {'id': 2, 'name': 'Bob'}
], ttl=300)  # 5 minute TTL

# Retrieve data
session = cache.get('session:abc123')
if session:
    print(f"User: {session['username']}")

users = cache.get('api_response:users')
if users:
    print(f"User count: {len(users)}")

Java Implementation Example

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class PocketCache<K, V> {
    private static class CacheItem<V> {
        final V value;
        final long expiresAt;
        volatile long lastAccessed;
        
        CacheItem(V value, long ttlMs) {
            this.value = value;
            this.expiresAt = System.currentTimeMillis() + ttlMs;
            this.lastAccessed = System.currentTimeMillis();
        }
        
        boolean isExpired() {
            return System.currentTimeMillis() > expiresAt;
        }
    }
    
    private final ConcurrentHashMap<K, CacheItem<V>> cache;
    private final int maxSize;
    private final long defaultTtl;
    private final ScheduledExecutorService cleanup;
    
    public PocketCache(int maxSize, long defaultTtlMs) {
        this.cache = new ConcurrentHashMap<>();
        this.maxSize = maxSize;
        this.defaultTtl = defaultTtlMs;
        
        // Periodic cleanup
        this.cleanup = Executors.newSingleThreadScheduledExecutor();
        this.cleanup.scheduleAtFixedRate(this::cleanupExpired, 
                                        60, 60, TimeUnit.SECONDS);
    }
    
    public void put(K key, V value) {
        put(key, value, defaultTtl);
    }
    
    public void put(K key, V value, long ttlMs) {
        // Size limit check
        if (cache.size() >= maxSize) {
            evictLRU();
        }
        
        cache.put(key, new CacheItem<>(value, ttlMs));
    }
    
    public V get(K key) {
        CacheItem<V> item = cache.get(key);
        if (item == null || item.isExpired()) {
            cache.remove(key);
            return null;
        }
        
        item.lastAccessed = System.currentTimeMillis();
        return item.value;
    }
    
    public boolean containsKey(K key) {
        return get(key) != null;
    }
    
    public boolean remove(K key) {
        return cache.remove(key) != null;
    }
    
    public void clear() {
        cache.clear();
    }
    
    public int size() {
        return cache.size();
    }
    
    private void evictLRU() {
        K lruKey = null;
        long oldestAccess = Long.MAX_VALUE;
        
        for (var entry : cache.entrySet()) {
            long lastAccessed = entry.getValue().lastAccessed;
            if (lastAccessed < oldestAccess) {
                oldestAccess = lastAccessed;
                lruKey = entry.getKey();
            }
        }
        
        if (lruKey != null) {
            cache.remove(lruKey);
        }
    }
    
    private void cleanupExpired() {
        cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
    }
    
    public void shutdown() {
        cleanup.shutdown();
    }
}

// Usage example
public class CacheExample {
    public static void main(String[] args) {
        PocketCache<String, Object> cache = new PocketCache<>(1000, 300000); // 5 minute TTL
        
        // Cache user information
        cache.put("user:1000", Map.of(
            "name", "John Doe",
            "email", "[email protected]",
            "role", "admin"
        ));
        
        // Cache API response (short TTL)
        cache.put("api:products", Arrays.asList(
            Map.of("id", 1, "name", "Laptop"),
            Map.of("id", 2, "name", "Mouse")
        ), 60000); // 1 minute TTL
        
        // Retrieve data
        Object user = cache.get("user:1000");
        if (user != null) {
            System.out.println("User info: " + user);
        }
        
        Object products = cache.get("api:products");
        if (products != null) {
            System.out.println("Product info: " + products);
        }
        
        // Cleanup
        cache.shutdown();
    }
}

PHP Implementation Example

<?php
class PocketCache {
    private $cache = [];
    private $maxSize;
    private $defaultTtl;
    
    public function __construct($maxSize = 1000, $defaultTtl = 3600) {
        $this->maxSize = $maxSize;
        $this->defaultTtl = $defaultTtl;
    }
    
    public function set($key, $value, $ttl = null) {
        $ttl = $ttl ?: $this->defaultTtl;
        $expiresAt = time() + $ttl;
        
        // Size limit check
        if (count($this->cache) >= $this->maxSize) {
            $this->evictOldest();
        }
        
        $this->cache[$key] = [
            'value' => $value,
            'expires_at' => $expiresAt,
            'accessed_at' => time()
        ];
    }
    
    public function get($key) {
        if (!isset($this->cache[$key])) {
            return null;
        }
        
        $item = $this->cache[$key];
        
        // Expiration check
        if (time() > $item['expires_at']) {
            unset($this->cache[$key]);
            return null;
        }
        
        // Update access time
        $this->cache[$key]['accessed_at'] = time();
        return $item['value'];
    }
    
    public function has($key) {
        return $this->get($key) !== null;
    }
    
    public function delete($key) {
        if (isset($this->cache[$key])) {
            unset($this->cache[$key]);
            return true;
        }
        return false;
    }
    
    public function clear() {
        $this->cache = [];
    }
    
    public function size() {
        return count($this->cache);
    }
    
    private function evictOldest() {
        $oldestKey = null;
        $oldestTime = time();
        
        foreach ($this->cache as $key => $item) {
            if ($item['accessed_at'] < $oldestTime) {
                $oldestTime = $item['accessed_at'];
                $oldestKey = $key;
            }
        }
        
        if ($oldestKey) {
            unset($this->cache[$oldestKey]);
        }
    }
    
    public function cleanupExpired() {
        $currentTime = time();
        $expiredKeys = [];
        
        foreach ($this->cache as $key => $item) {
            if ($currentTime > $item['expires_at']) {
                $expiredKeys[] = $key;
            }
        }
        
        foreach ($expiredKeys as $key) {
            unset($this->cache[$key]);
        }
        
        return count($expiredKeys);
    }
}

// Usage example
$cache = new PocketCache(500, 600); // Max 500 items, 10 minute TTL

// Cache database query results
$cache->set('popular_products', [
    ['id' => 1, 'name' => 'Laptop', 'views' => 1500],
    ['id' => 2, 'name' => 'Mouse', 'views' => 800],
    ['id' => 3, 'name' => 'Keyboard', 'views' => 600]
], 300); // 5 minute TTL

// Cache user settings
$cache->set('user_settings:1000', [
    'theme' => 'dark',
    'language' => 'en',
    'notifications' => true
]);

// Retrieve from cache
$products = $cache->get('popular_products');
if ($products) {
    echo "Popular products count: " . count($products) . "\n";
}

$settings = $cache->get('user_settings:1000');
if ($settings) {
    echo "User theme: " . $settings['theme'] . "\n";
}

// Statistics
echo "Cache size: " . $cache->size() . "\n";
$expired = $cache->cleanupExpired();
echo "Expired items removed: " . $expired . "\n";
?>