audit
This commit is contained in:
@@ -83,12 +83,15 @@ function onWeeklyReset(fn) {
|
||||
const firedThresholds = new Map();
|
||||
// key -> window type used for threshold clearing ("today" | "week" | "month")
|
||||
const firedThresholdWindows = new Map();
|
||||
// key -> last-seen timestamp; drives periodic sweep for keys that outlive their window reset.
|
||||
const firedThresholdLastSeen = new Map();
|
||||
|
||||
function clearFiredThresholdsForWindow(windowType) {
|
||||
for (const [key, mappedWindowType] of firedThresholdWindows.entries()) {
|
||||
if (mappedWindowType === windowType) {
|
||||
firedThresholds.delete(key);
|
||||
firedThresholdWindows.delete(key);
|
||||
firedThresholdLastSeen.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +101,7 @@ function shouldFireThreshold(key, ageMs, thresholdsMs, windowType) {
|
||||
if (!['today', 'week', 'month'].includes(windowType)) return null;
|
||||
|
||||
firedThresholdWindows.set(key, windowType);
|
||||
firedThresholdLastSeen.set(key, Date.now());
|
||||
|
||||
const firedForKey = firedThresholds.get(key) || new Set();
|
||||
const sortedThresholds = [...thresholdsMs].sort((a, b) => a - b);
|
||||
@@ -148,18 +152,49 @@ function clearEscalating(key) {
|
||||
escalatingCooldowns.delete(key);
|
||||
}
|
||||
|
||||
const ESCALATING_COOLDOWN_TTL_MS = 48 * 60 * 60 * 1000;
|
||||
const ESCALATING_CLEANUP_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
||||
const SWEEP_TTL_MS = 48 * 60 * 60 * 1000;
|
||||
const SWEEP_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
||||
|
||||
function cleanupStaleEscalatingCooldowns() {
|
||||
const cutoff = Date.now() - ESCALATING_COOLDOWN_TTL_MS;
|
||||
function cleanupStaleEscalatingCooldowns(now = Date.now()) {
|
||||
const cutoff = now - SWEEP_TTL_MS;
|
||||
for (const [key, state] of escalatingCooldowns.entries()) {
|
||||
const lastUsed = state.lastUsed || state.lastFireAtMs || state.startedAtMs || 0;
|
||||
if (lastUsed < cutoff) escalatingCooldowns.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(cleanupStaleEscalatingCooldowns, ESCALATING_CLEANUP_INTERVAL_MS).unref?.();
|
||||
// Sweep every per-Map timestamp-bearing entry older than SWEEP_TTL_MS.
|
||||
// firedThresholds/firedThresholdWindows are cleared by windowType-resets;
|
||||
// this sweep covers keys whose window never resets under load.
|
||||
function sweepPatternStore(now = Date.now()) {
|
||||
const cutoff = now - SWEEP_TTL_MS;
|
||||
for (const [key, ts] of cooldowns.entries()) {
|
||||
if (ts < cutoff) cooldowns.delete(key);
|
||||
}
|
||||
for (const [key, ts] of staffLastSeen.entries()) {
|
||||
if (ts < cutoff) staffLastSeen.delete(key);
|
||||
}
|
||||
cleanupStaleEscalatingCooldowns(now);
|
||||
for (const [key, ts] of firedThresholdLastSeen.entries()) {
|
||||
if (ts < cutoff) {
|
||||
firedThresholds.delete(key);
|
||||
firedThresholdWindows.delete(key);
|
||||
firedThresholdLastSeen.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the module's sweep on the given trackInterval function.
|
||||
* Called once from the ready handler. Interval is unref'd so it never
|
||||
* blocks shutdown; trackInterval ensures handleShutdown clears it.
|
||||
*/
|
||||
function startSweeps(trackInterval) {
|
||||
const handle = setInterval(() => sweepPatternStore(), SWEEP_INTERVAL_MS);
|
||||
if (typeof handle.unref === 'function') handle.unref();
|
||||
if (typeof trackInterval === 'function') trackInterval(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
function scheduleDailyReset() {
|
||||
setTimeout(() => {
|
||||
@@ -243,5 +278,9 @@ module.exports = {
|
||||
isOnCooldown,
|
||||
updateStaffLastSeen,
|
||||
getStaffLastSeen,
|
||||
isStaffRecentlyActive
|
||||
isStaffRecentlyActive,
|
||||
startSweeps,
|
||||
sweepPatternStore,
|
||||
// test-only exports
|
||||
_internals: { cooldowns, staffLastSeen, escalatingCooldowns, firedThresholds, firedThresholdWindows, firedThresholdLastSeen, SWEEP_TTL_MS }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user