Spaces:
Paused
Paused
| import { MinPriorityQueue } from '@datastructures-js/priority-queue'; | |
| import { Store } from './base-store.js'; | |
| // minimum delay between sweeps to avoid repeatedly | |
| // sweeping entries close in proximity one by one. | |
| const MIN_THRESHOLD_MS = 2500; | |
| export default class MemoryStore extends Store { | |
| #store = new Map(); | |
| #timeouts = new MinPriorityQueue/*<{ t: number, k: unknown }>*/((obj) => obj.t); | |
| #nextSweep = { id: null, t: null }; | |
| constructor(name) { | |
| super(name); | |
| } | |
| _has(key) { | |
| return this.#store.has(key); | |
| } | |
| _get(key) { | |
| const val = this.#store.get(key); | |
| return val === undefined ? null : val; | |
| } | |
| _set(key, val, exp_sec = -1) { | |
| if (this.#store.has(key)) { | |
| this.#timeouts.remove(o => o.k === key); | |
| } | |
| if (exp_sec > 0) { | |
| const exp = 1000 * exp_sec; | |
| const timeout_at = +new Date() + exp; | |
| this.#timeouts.enqueue({ k: key, t: timeout_at }); | |
| } | |
| this.#store.set(key, val); | |
| this.#reschedule(); | |
| } | |
| #reschedule() { | |
| const current_time = new Date().getTime(); | |
| const time = this.#timeouts.front()?.t; | |
| if (!time) { | |
| return; | |
| } else if (time < current_time) { | |
| return this.#sweepNow(); | |
| } | |
| const sweep = this.#nextSweep; | |
| if (sweep.id === null || sweep.t > time) { | |
| if (sweep.id) { | |
| clearTimeout(sweep.id); | |
| } | |
| sweep.t = time; | |
| sweep.id = setTimeout( | |
| () => this.#sweepNow(), | |
| Math.max(MIN_THRESHOLD_MS, time - current_time) | |
| ); | |
| sweep.id.unref(); | |
| } | |
| } | |
| #sweepNow() { | |
| while (this.#timeouts.front()?.t < new Date().getTime()) { | |
| const item = this.#timeouts.dequeue(); | |
| this.#store.delete(item.k); | |
| } | |
| this.#nextSweep.id = null; | |
| this.#nextSweep.t = null; | |
| this.#reschedule(); | |
| } | |
| } | |