Read at RAM speed
get, has, size, and
iteration are synchronous operations on an in-process Map.
A tiny TypeScript library that gives each Node.js process a fast local
Map—then coalesces and synchronizes changes through Redis.
npm i redis-dist-map ioredis
EVENT 18446744073709551615-0 SYNCED
Reads and writes touch memory immediately. Redis receives one coalesced, durable patch every 50 milliseconds.
get, has, size, and
iteration are synchronous operations on an in-process Map.
Repeated updates collapse by key, then the Hash and ten-second Stream history update in one Redis transaction.
Blocking Stream readers apply ordered events to every live instance. Revision gaps trigger a snapshot only when recovery is needed.
Let one process handle the market feed. Every API instance gets the latest price in memory—without polling or a Redis call per read.
PriceTick { ask: number; bid: number; volume: number }
Publish only. No snapshot, local Map, or XREAD connection.
import { createDistributedMapWriter } from
"redis-dist-map";
const writer = createDistributedMapWriter<PriceTick>(
"market:prices",
{ client: redis },
);
feed.on("tick", ({ symbol, ask, bid, volume }) => {
writer.set(symbol, { ask, bid, volume });
});
Stream updates arrive before the next customer request.
import { createDistributedMap } from
"redis-dist-map";
const tickers = await createDistributedMap<PriceTick>(
"market:prices",
{ client: redis },
);
// synchronous local read — no Redis round trip
const ask = tickers.get("AAPL")?.ask;
Ingest once. Fan out automatically. Every pricing endpoint reads the latest value at local Map speed.
Whichever API instance fills the cache first shares that response. Every other instance can answer from its own memory.
TickerPage { title: string; summary: string }
Load the response, return it, and share it automatically.
import { createDistributedMap } from
"redis-dist-map";
const cache = await createDistributedMap<TickerPage>(
"cache:ticker-pages",
{ client: redis },
);
app.get("/ticker/:name", async (request) => {
const { name } = request.params;
const hit = cache.get(name);
if (hit !== undefined) return hit;
const ticker = await db.getWikipage(name);
cache.set(name, ticker);
return ticker;
});
The load balancer can send the next request anywhere.
import { createDistributedMap } from
"redis-dist-map";
const cache = await createDistributedMap<TickerPage>(
"cache:ticker-pages",
{ client: redis },
);
app.get("/ticker/:name", async (request) => {
const { name } = request.params;
// already synced from instance 01
const hit = cache.get(name);
if (hit !== undefined) return hit;
const ticker = await db.getWikipage(name);
cache.set(name, ticker);
return ticker;
});
Same route, every instance. One cache fill turns into local hits across the whole API fleet.
Every API instance increments the same atomic counter. Request 101 is rejected wherever the load balancer sends it.
SharedCounter { inc(key): Promise<number> }
No local guess, write buffer, or synchronization delay.
import { createSharedCounter } from
"redis-dist-map";
const requests = createSharedCounter(
"rate-limit:api",
{ client: redis, ttlMs: 60_000 },
);
app.use(async (req, res, next) => {
const current = await requests.inc(req.user.id);
if (current > 100) {
return res.status(429).send({
error: "RATE_LIMITED",
ok: false,
});
}
next();
});
Any server sees the same count and returns the same decision.
{ "error": "RATE_LIMITED", "ok": false }
INCR and first-write expiry run in one Lua script. No increment is buffered, coalesced, or lost between API instances.
Bring an ioredis client. Pick a namespaced key. The first snapshot and background listener are ready when the promise resolves.
import Redis from "ioredis";
import { createDistributedMap } from
"redis-dist-map";
const redis = new Redis(process.env.REDIS_URL);
const flags = await createDistributedMap<boolean>(
"app:prod:flags",
{ client: redis },
);
flags.onChange("new-checkout", (enabled) =>
console.log(enabled)
);
flags.set("new-checkout", true);
// synchronous — no Redis round trip
flags.get("new-checkout"); // true
// explicit durability boundary
await flags.flush();
createSharedCounter()→ counterCreate an atomic Redis counter namespace.
counter.inc(key)→ Promise<number>Increment immediately with optional first-write expiry.
.get(key)→ T | undefinedRead from the local cache.
.onChange(key, fn)→ unsubscribe()React to local and remote changes.
.set(key, value)→ voidUpdate locally and queue a value.
.delete(key)→ booleanDelete locally and queue the key.
.clear()→ voidClear locally and queue the reset.
.flush()→ Promise<void>Persist and broadcast pending changes.
.synchronize()→ Promise<void>Reload the Redis snapshot.
.destroy()→ Promise<void>Stop listening and clean up.
createDistributedMapWriter()→ writerPublish without a snapshot or XREAD connection.
One familiar abstraction. Every process in sync.