FAIL3.5
Loading cell…
Loading cell…
Write a TypeScript function named createIdempotencyGuard.
Requirements:
key, ttlMs, and an async handler function.Output format: { "code": "...", "explanation": "...", "complexity": { "time": "...", "space": "..." }, "tests": ["...", "...", "...", "...", "..."] }
{
"code": "interface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\ninterface GuardOptions<T> {\n key: string;\n ttlMs: number;\n handler: () => Promise<T>;\n}\n\nexport function createIdempotencyGuard() {\n const cache = new Map<string, CacheEntry<unknown>>();\n const inflight = new Map<string, Promise<unknown>>();\n\n return async function guard<T>(options: GuardOptions<T>): Promise<T> {\n const { key, ttlMs, handler } = options;\n\n // 1. Check for a valid cached result\n const cached = cache.get(key);\n if (cached && cached.expiresAt > Date.now()) {\n return cached.value as T;\n }\n // Clean up expired entry\n if (cached) {\n cache.delete(key);\n }\n\n // 2. Check for an in-flight request with the same key\n const pending = inflight.get(key);\n if (pending) {\n return pending as Promise<T>;\n }\n\n // 3. Execute the handler and track the in-flight promise\n const promise = handler().then(\n (result) => {\n cache.set(key, { value: result, expiresAt: Date.now() + ttlMs });\n inflight.delete(key);\n return result;\n },\n (error) => {\n inflight.delete(key);\n throw error;\n }\n );\n\n inflight.set(key, promise);\n return promise;\n };\n}",
"explanation": "createIdempotencyGuard returns a closure that maintains two internal Maps: one for cached results (with expiration timestamps) and one for in-flight promises. On each call, it first checks if a non-expired cached value exists for the given key and returns it immediately. If not, it checks whether a request for that key is already in-flight; if so, it returns the existing promise, ensuring the handler is not invoked again concurrently. If neither condition is met, it invokes the handler, stores the resulting promise in the inflight map, and upon success caches the result with a computed expiration time (Date.now() + ttlMs) and removes it from inflight. On failure, the promise is removed from inflight without caching, so subsequent calls will retry. All map operations are synchronous, and JavaScript's single-threaded event loop guarantees no race conditions between the cache/inflight checks and the inflight.set call.",
"complexity": {
"time": "O(1) per call — Map.get, Map.set, Map.delete, and Date.now() are all O(1) average-case operations. The handler's own execution time is not included.",
"space": "O(U + I) where U is the number of unique keys with unexpired cached results and I is the number of keys with in-flight requests. In the worst case this is O(N) where N is the total number of distinct keys ever used, though expired entries are lazily evicted on access."
},
"tests": [
"async function testConcurrency() {\n const guard = createIdempotencyGuard();\n let callCount = 0;\n const handler = async () => {\n callCount++;\n await new Promise(r => setTimeout(r, 50));\n return 'result';\n };\n const [r1, r2, r3] = await Promise.all([\n guard({ key: 'k1', ttlMs: 5000, handler }),\n guard({ key: 'k1', ttlMs: 5000, handler }),\n guard({ key: 'k1', ttlMs: 5000, handler }),\n ]);\n console.assert(callCount === 1, `Expected 1 call, got ${callCount}`);\n console.assert(r1 === 'result' && r2 === 'result' && r3 === 'result');\n console.log('testConcurrency passed');\n}",
"async function testCaching() {\n const guard = createIdempotencyGuard();\n let callCount = 0;\n const handler = async () => { callCount++; return 'cached-value'; };\n const r1 = await guard({ key: 'k2', ttlMs: 5000, handler });\n const r2 = await guard({ key: 'k2', ttlMs: 5000, handler });\n console.assert(callCount === 1, `Expected 1 call, got ${callCount}`);\n console.assert(r1 === 'cached-value' && r2 === 'cached-value');\n console.log('testCaching passed');\n}",
"async function testExpiry() {\n const guard = createIdempotencyGuard();\n let callCount = 0;\n const handler = async () => { callCount++; return `val-${callCount}`; };\n const r1 = await guard({ key: 'k3', ttlMs: 50, handler });\n console.assert(r1 === 'val-1');\n await new Promise(r => setTimeout(r, 8
0/2 checks passed