Databases 16 min read

Why Do Major Tech Companies Shun the Popular Delayed Double‑Delete Cache Strategy?

The article examines the delayed double‑delete cache invalidation technique, reveals its hidden risk of cache‑penetration‑induced database load, compares Facebook's lease‑based solution and Uber's version‑number approach, and advises when the method is appropriate for high‑traffic systems.

dbaplus Community
dbaplus Community
dbaplus Community
Why Do Major Tech Companies Shun the Popular Delayed Double‑Delete Cache Strategy?

Problem with delayed double‑delete

Cache‑aside patterns often delete a cache entry after a DB write and add a delayed second delete (1–2 s) to reduce inconsistency windows. The two rapid deletions can both miss the cache, forcing the primary DB to serve the request. In low‑traffic services this extra load is tolerable, but in high‑traffic services the sudden DB surge can be intolerable.

Delayed double‑delete illustration
Delayed double‑delete illustration

Facebook lease mechanism

Facebook’s 2013 paper “Scaling Memcache at Facebook” proposes a lease (lock‑like) mechanism.

Lease acquisition

If a key is missing, Redis returns a 64‑bit token and stores it under a lease key.

The client must present the token on subsequent writes; Redis validates the token before storing data.

Other requests must wait for the lease to expire before acquiring a new one.

Lua script for get (lease acquisition):

local key = KEYS[1]
local token = ARGV[1]
local value = redis.call('get', key)
if not value then
    redis.replicate_commands()
    local lease_key = 'lease:'..key
    redis.call('set', lease_key, token)
    return {false, false}
else
    return {value, true}
end

Lua script for set (lease validation):

local key = KEYS[1]
local token = ARGV[1]
local value = ARGV[2]
local lease_key = 'lease:'..key
local lease_value = redis.call('get', lease_key)
if lease_value == token then
    redis.replicate_commands()
    redis.call('set', key, value)
    return {value, true}
else
    return {false, false}
end

Application impact:

Operations must be performed via EVAL instead of raw Redis commands.

Redis returns an array; callers must parse it.

Clients generate a token per request and follow a three‑step workflow (get → optional DB load → set).

Java wrapper for the result:

public class EvalResult {
    String value;
    boolean effect;
    public EvalResult(List<?> args) {
        value = (String) args.get(0);
        effect = args.get(1) != null && 1 == (long) args.get(1);
    }
}

Java lease‑aware client:

public class LeaseWrapper extends Jedis implements CacheCommands {
    private final Jedis jedis;
    private final TokenGenerator tokenGenerator = () -> UUID.randomUUID().toString();
    private final ThreadLocal<String> tokenHolder = new ThreadLocal<>();

    public LeaseWrapper(Jedis jedis) { this.jedis = jedis; }

    @Override
    public String get(String key) {
        String token = tokenGenerator.get();
        tokenHolder.set(token);
        Object result = jedis.eval(LuaScripts.leaseGet(), List.of(key), List.of(token));
        EvalResult er = new EvalResult((List<?>) result);
        return er.effect ? er.value : null;
    }

    @Override
    public String set(String key, String value) {
        String token = tokenHolder.get();
        tokenHolder.remove();
        Object result = jedis.eval(LuaScripts.leaseSet(), List.of(key), List.of(token, value));
        EvalResult er = new EvalResult((List<?>) result);
        return er.effect ? er.value : null;
    }
}
Lease mechanism diagram
Lease mechanism diagram

Uber version‑number solution

Uber’s 2024 blog “How Uber Serves Over 40 Million Reads Per Second from Online Storage Using an Integrated Cache” stores a timestamp version alongside each record. Writes compare the incoming version with the stored version; only newer data overwrites the cache.

Lua script for versioned set (string value):

local key = KEYS[1]
local value = ARGV[1]
local current_version = ARGV[2]
local version_key = 'version:'..key
local version_value = redis.call('get', version_key)
if version_value == false or version_value < current_version then
    redis.call('mset', version_key, current_version, key, value)
    return {value, true}
else
    return {false, false}
end

Java wrapper for versioned writes:

public class VersionWrapper extends Jedis implements CacheCommands {
    private final Jedis jedis;
    public VersionWrapper(Jedis jedis) { this.jedis = jedis; }

    public String set(String key, String value, String version) {
        Object result = jedis.eval(LuaScripts.versionSet(),
                                   List.of(key),
                                   List.of(value, version));
        EvalResult er = new EvalResult((List<?>) result);
        return er.effect ? er.value : null;
    }
}

Uber combines this with an asynchronous Flux‑based component that introduces a second‑level delay of a few seconds, reducing cache‑penetration risk while keeping latency acceptable for most services.

Uber version‑number diagram
Uber version‑number diagram

Trade‑off analysis

Delayed double‑delete is simple but can cause a short‑term DB overload in high‑traffic scenarios.

Lease mechanism prevents concurrent cache misses at the cost of added client complexity (token management, Lua scripts, EVAL calls).

Version‑number approach avoids stale writes but requires storing an extra version key and performing version comparison on each write; it fits naturally with asynchronous update pipelines.

Conclusion

For low‑traffic services the delayed double‑delete may be acceptable. High‑traffic services should consider alternatives such as Facebook’s lease mechanism or Uber’s version‑based updates, selecting the approach that matches traffic profile, team expertise, and infrastructure capabilities.

JavaRedisCache ConsistencyLuaversioningDelayed Double DeleteLease
dbaplus Community
Written by

dbaplus Community

Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.