Skip to content
Dashboard

Preventing the stampede: Request collapsing in the Vercel CDN

Software Engineering Intern

Link to headingHow caching normally works

Cache lookup sequence across layers in the Vercel CDN.Cache lookup sequence across layers in the Vercel CDN.
Cache lookup sequence across layers in the Vercel CDN.
Without coordination, each request triggers its own regeneration.Without coordination, each request triggers its own regeneration.
Without coordination, each request triggers its own regeneration.
One request regenerates, others wait and receive the cached result.One request regenerates, others wait and receive the cached result.
One request regenerates, others wait and receive the cached result.

Link to headingWhen collapsing can be applied

Link to headingDistributed locking

function createDistributedLock(cacheKey) {
const nodeLock = createNodeLock(cacheKey);
const regionalLock = createRegionalLock(cacheKey);
return combineLocks([nodeLock, regionalLock]);
}
async function respond(request) {
const cacheKey = getCacheKey(request);
const cachedResponse = await cache.get(cacheKey);
if (cachedResponse) return cachedResponse;
const lock = createDistributedLock(cacheKey);
await lock.lock();
const response = await invokeFunction(request);
lock.unlock();
return response;
}

Lock-based flow: check cache, acquire locks, regenerate if needed.

Link to headingDouble-checked locking

async function respond(request) {
const cacheKey = getCacheKey(request);
const cachedResponse = await cache.get(cacheKey);
if (cachedResponse) return cachedResponse;
const lock = createDistributedLock(cacheKey);
await lock.lock();
let cachedResponse = await cache.get(cacheKey);
if (cachedResponse) return cachedResponse;
const functionResponse = await invokeFunction(request);
// set cache in background so we can return response immediately
(async () => {
await cache.set(cacheKey, functionResponse);
lock.unlock();
})()
return functionResponse;
}

Double-checked locking: only regenerate if the cache is still empty after lock acquisition.

Link to headingHandling failures

function createDistributedLock(cacheKey) {
const nodeLock = createNodeLock(cacheKey, { timeout: 3000 });
const regionalLock = createRegionalLock(cacheKey, { timeout: 3000 });
return combineLocks([nodeLock, regionalLock]);
}

Lock creation with timeouts to prevent requests from waiting indefinitely.

Link to headingProduction impact