Cache Library
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";
?>