Disclaimer: As a non-native English speaker, I used AI to help structure and polish this article.
A few dayss ago, I shared Implementing a Zero-Allocation S3-FIFO Cache in Node.js—an exploratory v0.1 release demonstrating how the S3-FIFO (Simple and Scalable Scan-Resistant FIFO) caching algorithm can be implemented using pre-allocated TypedArray
s to eliminate Garbage Collection (GC) pressure in Node.js.
Today, after extensive stress testing, architectural hardening, and 100% unit test coverage, I'm excited to announce the release of ** s3fifo v1.0.0**! 🚀
Traditional LRU (Least Recently Used) caches suffer from two major production drawbacks:
set()
, triggering frequent GC s under high-throughput workloads.
s3fifo
achieves get
/set
cycles by backing these queues with contiguous TypedArrays (Uint32Array
, Float64Array
) and reusable index pools. #
What's New in v1.0.0? (Production Readiness) #
While v0.1 focused on core algorithm speed, dump
& load
)
Prevent s3fifo
1.0 supports serializing active resident entries alongside their original creation timestamps and remaining TTL:
import fs from "node:fs";
import { S3Fifo } from "s3fifo";
const cache = new S3Fifo<string>({ max: 10000 });
// 1. Export active cache items (optionally filter out temporary keys)
const dumpData = cache.dump((key, value) => !key.startsWith("temp:"));
fs.writeFileSync("cache-snapshot.json", JSON.stringify(dumpData));
// 2. On server startup / pre-warming: restore cache state instantly
const snapshot = JSON.parse(fs.readFileSync("cache-snapshot.json", "utf-8"));
cache.load(snapshot);
dispose
Callback) When cache items are evicted, overwritten, or cleared, you often need to release external resources (e.g., closing file descriptors, destroying DB handles, or tracking eviction metrics).
const cache = new S3Fifo<Buffer>({
max: 500,
dispose: (key, buffer, reason) => {
console.log(`Key ${key} removed due to: ${reason}`); // 'evict' | 'set' | 'delete' | 'clear'
// Safely free native memory or resources
},
});
🛡️
Re-Entrancy Protection:dispose
callbacks are safely deferred until the cache operation completes, preventing internal state corruption if a callback invokescache.set()
orcache.delete()
recursively.## 3. 🔍 Side-Effect-Free Inspection (
peek
)Need to check a cached value for logging, health checks, or monitoring without bumping frequency counters or altering eviction status?
peek()
allows pure, side-effect-free reading:
// Does not modify S3-FIFO frequency bit fields or TTL timestamps
const val = cache.peek("user:1001");
s3fifo
1.0 integrates seamlessly with JavaScript's native iteration protocols:
// Standard JS Map style iterators
for (const [key, value] of cache) {
console.log(key, value);
}
const keys = Array.from(cache.keys());
const values = Array.from(cache.values());
const entries = Array.from(cache.entries());
cache.forEach((val, key) => {
/* ... */
});
close
)
In serverless, hot-re (HMR), or test environments, background timers can prevent process termination. The close()
method clears active background TTL tickers, releases memory buffers, and prevents memory leaks:
// Clean teardown on shutdown
cache.close();
console.log(cache.isClosed); // true
Tested on a Zipfian distribution (skew 0.99
, working set of 100,000
keys) comparing s3fifo
v1.0 to Node's popular lru-cache
:
| Cache Size (% of Pool) | lru-cache | s3fifo |
|---|---|---|
| 1% | 48.90% | 58.30% |
| 5% | 65.00% | 71.10% |
| 10% | 72.30% | 76.40% |
| 25% | 82.10% | 82.70% |
| 50% | 89.00% | |
| 86.30% |
| Cache Size (% of Pool) | lru-cache | s3fifo |
|---|---|---|
| 1% | 10.8M | 15.5M |
| 5% | 10.8M | 14.4M |
| 10% | 10.2M | 14.3M |
| 25% | 10.3M | 13.5M |
| 50% | 10.3M | 14.7M |
get
/set
operations. Install via npm:
npm install s3fifo
Basic usage:
import { S3Fifo } from "s3fifo";
const cache = new S3Fifo<string>({
max: 1000,
ttl: 60000, // 60s global TTL
});
cache.set("session:abc", "user_data");
console.log(cache.get("session:abc")); // 'user_data'
console.log(cache.size); // 1
s3fifo
a try! Bug reports, feedback, and GitHub stars are greatly appreciated! 🙏