This issue is raised as part of GSSoC'26. Kindly consider assigning it to me.
Summary
In src/middleware.ts, the pruneMemoryBuckets function only removes
keys where ALL timestamps have expired:
if (active.length === 0) {
memoryBuckets.delete(key);
}
Keys with even one active timestamp are never cleaned of their
older stale timestamps during pruning. Over time, a frequently
accessed key accumulates thousands of stale timestamps that are
filtered out on every request but never actually removed from memory.
Additionally, pruning is skipped entirely when the map has fewer
than 500 keys, so per-key memory growth happens invisibly in
low-traffic scenarios.
Steps to Reproduce
- Make 1000+ requests from the same IP over multiple windows
- Inspect memoryBuckets — the key still holds all old timestamps
- Pruning never removes stale timestamps within active keys
Expected Behavior
Stale timestamps within active keys should be cleaned up during
the pruning pass, not just keys with zero active timestamps.
Actual Behavior
Active keys accumulate stale timestamps indefinitely, causing
silent per-key memory growth.
Fix
Update the pruning logic to always clean stale timestamps from
active keys, not just delete empty ones:
for (const [key, values] of Array.from(memoryBuckets.entries())) {
const active = values.filter(
(timestamp: number) => timestamp > cutoff
);
if (active.length === 0) {
memoryBuckets.delete(key);
} else {
memoryBuckets.set(key, active); // always update, not just delete
}
}
This issue is raised as part of GSSoC'26. Kindly consider assigning it to me.
Summary
In
src/middleware.ts, thepruneMemoryBucketsfunction only removeskeys where ALL timestamps have expired:
if (active.length === 0) {
memoryBuckets.delete(key);
}
Keys with even one active timestamp are never cleaned of their
older stale timestamps during pruning. Over time, a frequently
accessed key accumulates thousands of stale timestamps that are
filtered out on every request but never actually removed from memory.
Additionally, pruning is skipped entirely when the map has fewer
than 500 keys, so per-key memory growth happens invisibly in
low-traffic scenarios.
Steps to Reproduce
Expected Behavior
Stale timestamps within active keys should be cleaned up during
the pruning pass, not just keys with zero active timestamps.
Actual Behavior
Active keys accumulate stale timestamps indefinitely, causing
silent per-key memory growth.
Fix
Update the pruning logic to always clean stale timestamps from
active keys, not just delete empty ones:
for (const [key, values] of Array.from(memoryBuckets.entries())) {
const active = values.filter(
(timestamp: number) => timestamp > cutoff
);
if (active.length === 0) {
memoryBuckets.delete(key);
} else {
memoryBuckets.set(key, active); // always update, not just delete
}
}