Skip to content

Step 1 of 3 · Reading · ~2 min

Conditional Writes

String Commands

SET NX & XX — Conditional Writes

Plain SET always succeeds. Real applications frequently need something stronger: "only write this if nobody else already has," or "only update this if it already exists." Redis exposes both as flags on SET itself, rather than as separate commands — an important design choice worth understanding before you implement it.

The two flags

  • SET key value NXNot eXists: write only if the key is currently absent. Returns +OK\r\n on success, $-1\r\n (null) if the key already existed and nothing was written.
  • SET key value XX — eXists: write only if the key is currently present. Returns +OK\r\n on success, $-1\r\n if the key didn't exist.

Note both "conditional failure" cases return null, not an error — failing the condition is a normal, expected outcome (like GET on a missing key), not something's gone wrong.

Why NX matters: distributed locking

SET lock_key token NX is the textbook building block for a distributed lock: only one client's SET can succeed when the key is absent, so "I got +OK" means "I hold the lock." This is precisely why the check-and-write must happen as a single atomic command rather than a client doing EXISTS then SET — two separate round trips have a race window where two clients could both see the key absent and both proceed to write. Because your Redis clone processes one command to completion before reading the next, implementing NX as a single handler function gives you this atomicity for free.

Implementation

Parse the flags out of the trailing arguments before deciding what to do:

python

Edge cases

  • NX and XX together are mutually exclusive in real Redis — SET key val NX XX returns a syntax error. Decide whether your implementation enforces this or just lets XX's check run after NX's (which would always fail since a key can't simultaneously not-exist and exist) — either way, don't silently succeed.
  • Flags are case-insensitive just like command names: nx, Nx, NX should all work — normalize with .upper() as shown above.
  • Order of arguments: real Redis also allows EX/PX alongside NX/XX in the same SET call (you'll add that in the next lesson) — structure your flag parsing as a generic loop over rest now so it's easy to extend rather than hard-coding "flags are always at position 2 and 3."
  • Don't confuse "returns null because the condition failed" with "returns null because of an error" — these must produce the exact same byte pattern ($-1\r\n) as a GET miss, since that's what the null bulk string means in RESP.
Up nextINCR & DECR — Atomic CountersString Commands

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…