Memcached ADD vs SET: When to Use Each Command for Optimal Cache Management


3 views

html

In Memcached, both ADD and SET commands store data, but their behavior differs in one crucial aspect:

// SET will always store the value, overwriting if key exists
memcached.set("user:1001", "{name:'John', email:'john@example.com'}", expirationTime);

// ADD will only store if the key doesn't exist
memcached.add("user:1001", "{name:'John', email:'john@example.com'}", expirationTime);

When to use SET:

  • When you need to unconditionally update a cached value
  • For cache warming or forced refreshes
  • When implementing cache-aside pattern

When to use ADD:

  • Implementing distributed locks
  • Preventing cache stampedes (thundering herd problem)
  • Maintaining atomic counters

Here's how ADD helps prevent multiple concurrent cache misses:

function getExpensiveData(key) {
    // Try to get cached data first
    let data = memcached.get(key);
    
    if (!data) {
        // Only one process will succeed in adding the lock
        if (memcached.add(key + "_lock", "1", 5)) {
            // Generate expensive data
            data = expensiveDatabaseQuery();
            
            // Cache the data
            memcached.set(key, data, 3600);
            
            // Release the lock
            memcached.delete(key + "_lock");
        } else {
            // Other processes wait and retry
            sleep(100);
            return getExpensiveData(key);
        }
    }
    return data;
}

While both commands have similar performance characteristics:

  • SET typically has slightly lower overhead as it doesn't need to check existence
  • ADD requires an existence check but prevents unnecessary overwrites
  • Network latency dominates both operations

Watch out for these scenarios:

// Problematic race condition:
if (!memcached.get("counter")) {
    // Multiple processes might pass this check
    memcached.set("counter", 1);
}

// Correct implementation using ADD:
memcached.add("counter", 1); // Only one will succeed

In Memcached, both ADD and SET are fundamental operations for storing data, but they behave differently in critical ways:

// SET will always write the value, regardless of existing key
memcached.set("user:1001", "{name:'John', role:'admin'}");

// ADD will only succeed if the key doesn't exist
memcached.add("config:latest_version", "v2.4.1");

Use ADD when you need to ensure initialization of a value exactly once:

  • Creating unique locks or flags
  • Initializing configuration values
  • Implementing "first-write-wins" scenarios
// Only set default config if it doesn't exist
if (!memcached.add("system:timezone", "UTC")) {
    console.log("Timezone already configured");
}

SET is your go-to for unconditional writes:

  • Updating existing values
  • Forcing value overwrites
  • General caching scenarios
// Update user session unconditionally
memcached.set("session:" + userId, sessionData, 3600);

While both operations have similar O(1) time complexity, ADD performs an extra existence check internally. In high-throughput systems:

  • Prefer SET when you expect frequent updates
  • Use ADD only when the uniqueness guarantee is required
  • Benchmark both operations with your specific workload

ADD provides atomic "check-and-set" behavior:

// Thread-safe initialization example
function initCounter() {
    if (memcached.add("global:counter", 0)) {
        return 0;
    }
    return memcached.get("global:counter");
}

Cache Stampede Prevention:

// Using ADD as a lock
function getExpensiveData(key) {
    let data = memcached.get(key);
    if (!data) {
        if (memcached.add(key + ":lock", "1", 5)) {
            data = fetchFromDatabase(key);
            memcached.set(key, data, 300);
            memcached.delete(key + ":lock");
        } else {
            // Another process is generating the data
            return null;
        }
    }
    return data;
}

Configuration Management:

// Using SET for config updates
function updateConfig(key, value) {
    memcached.set("config:" + key, value);
    logConfigChange(key, value);
}