// eslint-disable-next-line node/no-extraneous-import import Keyv from 'keyv'; /** * Debounces the execution of a function that takes an ID parameter, ensuring that it only runs once per second * for each ID across all containers/threads. Uses a combination of shared cache and a locking mechanism to achieve this. * * @param id - The ID parameter for the function being debounced * @param cache - The Redis cache instance used for locking and storing timestamps * @param fn - The function being debounced, which takes the ID parameter and returns a Promise * @param delay - The delay in milliseconds between each execution of the function */ export async function safeDebounce( id: string | number, cache: Keyv, fn: (id: string | number) => Promise, delay = 1000, _added = Date.now() ) { const lockKey = `lock:${id}`; const lastAddedKey = `added:${id}`; // Get the timestamp of the last action for this ID const latestAdded = await cache.get(lastAddedKey); if (latestAdded && _added < latestAdded) { // A newer action has been added, so we can ignore this one return; } await cache.set(lastAddedKey, _added, delay); // Check if the lock is already set for this ID const isLocked = await cache.get(lockKey); if (!isLocked) { // Set the lock await cache.set(lockKey, _added, delay); try { // Execute the function return await fn(id); } catch (e) { console.error(e); } } else { setTimeout(() => { safeDebounce(id, cache, fn, delay, _added); }, delay / 2); } }