Review an Asynchronous Cache for Races, Memory Growth, and Thread Safety
Company: Mercor
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: hard
Interview Round: Technical Screen
Review this JavaScript cache sketch. Identify its immediate defects, explain the concurrency problem around an asynchronous load, and describe how you would bound its memory use. Then contrast the synchronization requirements with a shared cache accessed by multiple C++ threads.
```javascript
const cache = new Map()
function retrieve(key, val) {
if (cache.has(key)) {
return cache.get(key)
}
const newVal = await longRunningFn(key)
cache.set(key)
return newVal
}
```
### Part 1 — Correctness and Asynchronous Interleaving
Explain the syntax/API issues and what happens when two calls miss the same key before either load completes. Propose a policy for sharing in-flight work and handling failed or outdated loads.
#### What This Part Should Cover
The missing async declaration, missing stored value, unused parameter, duplicate work, promise/result contracts, and invalidation races.
### Part 2 — Memory and Eviction
Explain why garbage collection does not automatically make this cache bounded. Discuss a capacity or expiration policy and how it interacts with in-flight loads.
#### What This Part Should Cover
Reachability, eviction, failure cleanup, value size, and observable cache behavior.
### Part 3 — Multithreaded Access
Explain what changes for a C++ map shared by several threads and where synchronization belongs.
#### What This Part Should Cover
Physical data-race protection, compound check/claim operations, shared in-flight state, and avoiding a global lock around slow I/O.
### Constraints
For the JavaScript baseline, use one event loop without shared-memory workers. That assumption does not rule out asynchronous interleaving. For the C++ contrast, assume genuine concurrent access to shared state. The required freshness and eviction policies remain design choices to declare.
### Clarifying Questions
- Should concurrent requests for one key share a load, and should failed loads be cached?
- Can callers invalidate or overwrite an entry while a load is running?
- Is the memory limit based on entry count or estimated bytes?
```hint Separate language threads from logical races
Two JavaScript calls can interleave across await even when their synchronous instructions do not execute simultaneously.
```
### What a Strong Answer Covers
- Immediate code defects and a consistent asynchronous return contract.
- Per-key coordination, stale-completion protection, and bounded retention.
- Correct distinctions between event-loop interleaving, C++ locking, and garbage collection.
### Follow-up Questions
- How would you prevent an older load from replacing a newer explicit write?
- How would you evict a value without causing every waiter to start another load?
Overview: Review JavaScript cache defects, shared in-flight loads, invalidation races, eviction, garbage collection, and the C++ multithreaded synchronization contrast.
Review this JavaScript cache sketch. Identify its immediate defects, explain the concurrency problem around an asynchronous load, and describe how you would bound its memory use. Then contrast the synchronization requirements with a shared cache accessed by multiple C++ threads.
const cache = new Map()
function retrieve(key, val) {
if (cache.has(key)) {
return cache.get(key)
}
const newVal = await longRunningFn(key)
cache.set(key)
return newVal
}
Part 1 — Correctness and Asynchronous Interleaving
Explain the syntax/API issues and what happens when two calls miss the same key before either load completes. Propose a policy for sharing in-flight work and handling failed or outdated loads.
What This Part Should Cover Guidance
The missing async declaration, missing stored value, unused parameter, duplicate work, promise/result contracts, and invalidation races.
Part 2 — Memory and Eviction
Explain why garbage collection does not automatically make this cache bounded. Discuss a capacity or expiration policy and how it interacts with in-flight loads.
What This Part Should Cover Guidance
Reachability, eviction, failure cleanup, value size, and observable cache behavior.
Part 3 — Multithreaded Access
Explain what changes for a C++ map shared by several threads and where synchronization belongs.
What This Part Should Cover Guidance
Physical data-race protection, compound check/claim operations, shared in-flight state, and avoiding a global lock around slow I/O.
Constraints
For the JavaScript baseline, use one event loop without shared-memory workers. That assumption does not rule out asynchronous interleaving. For the C++ contrast, assume genuine concurrent access to shared state. The required freshness and eviction policies remain design choices to declare.
Clarifying Questions Guidance
Should concurrent requests for one key share a load, and should failed loads be cached?
Can callers invalidate or overwrite an entry while a load is running?
Is the memory limit based on entry count or estimated bytes?
What a Strong Answer Covers Guidance
Immediate code defects and a consistent asynchronous return contract.
Per-key coordination, stale-completion protection, and bounded retention.
Correct distinctions between event-loop interleaving, C++ locking, and garbage collection.
Follow-up Questions Guidance
How would you prevent an older load from replacing a newer explicit write?
How would you evict a value without causing every waiter to start another load?