Skip to content

BuildNot yet confirmed elsewhere1 publisher3 min readPublished

Valkey 9.2's INCREX closes the crash gap that strands INCR/EXPIRE counters without a TTL

Valkey 9.2.0-rc1's new INCREX command left no counters without an expiry in one developer's 500-request crash test, against 11 for INCR then EXPIRE. Most of its speed gain comes from dropping one round trip, so the case for adopting it is correctness.

The Engineer · Build desk

How we use AISend a correction

What happened

  • INCREX first appears in the 9.2.0-rc1 release candidate, and the previous stable build, 9.1.2, rejects it as an unknown command.
  • The syntax takes an optional NX or XX flag, an EX, PX, EXAT or PXAT expiry, and a BYINT or BYFLOAT step that defaults to an increment of 1.
  • Across three runs of 2,000 fresh keys, INCREX ran 1.6x to 2x faster than INCR and EXPIRE sent as two blocking calls.
  • With the old pair pipelined into one network exchange, INCREX's lead shrank to between 9% and 21%.
  • Twenty threads making 100 increments each on one shared key reached exactly 2,000, and the first caller's 30-second TTL stayed in place.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Teams that already pipeline the pair or wrap it in MULTI/EXEC will see little speed change, so their migration case rests on the closed crash window and the single call.
  • constraint Code that carries EXPIRE's NX and XX habits over to INCREX will behave differently, because INCREX uses SET's convention for those flags, so flag use needs checking before rollout.
  • constraint Fleet inventory cannot use INFO server to find release-candidate nodes, since a 9.2.0-rc1 server reports itself as plain 9.2.0.

The pattern INCREX replaces is three lines of application code, as the post's author writes them [9]:

```python val = r.incr(key) if val == 1: r.expire(key, 60) ```

Here is what happens when the process dies between the second and third lines. The increment has landed and the key holds 1. The next request gets 2, fails the check and skips EXPIRE. So does every request after it [20]. The author wrote that each such crash leaves "exactly one permanently orphaned key, because the increment already landed and nothing will ever set its expiry" [13].

INCREX [5] has no in-between state for a crash to land in. Either the process dies before the single command is sent and nothing happened, or it dies after the command completed and the key already holds its new value and its TTL [14].

The crash test gave each request a 2% chance of failing after the first command, and the observed rate came out at 2.2% [12][19]. That rate does not carry straight into production. Only the request whose INCR returns 1 runs EXPIRE, so only a crash on that request strands a key [20]. A counter that takes many increments per window creates its key once. The production orphan rate is therefore the crash rate on first increments, and each orphan persists [13].

The speed numbers came from Docker containers on one host, driven by redis-py 8.1.0 over localhost TCP [10]. The pipelined control run is the best thing in the post. It removes the second network wait, and what is left is the server-side difference [2]. That pipeline also drops the conditional and queues INCR and EXPIRE on every call, with transaction=False [11]. That is fine for a benchmark. In a limiter, an EXPIRE on every request sets the 60-second deadline again each time, so the window no longer starts at the first increment [21]. The post's own code makes the author's point that pipelining a conditional increment "is awkward enough that people skip it" [3].

I'd expect the blocking pattern's penalty to grow on a real network. It waits on the network twice per request, and a localhost round trip is about as short as round trips get [9][10].

Fixed-window quotas depend on later increments leaving the first TTL alone, which is the behaviour the 20-thread run showed [15]. The post does not show which flags those threads passed. The plain EX form's behaviour on an existing key needs its own check before a quota counter depends on it.

The build tested is a release candidate, the newest valkey/valkey tag on Docker Hub when the post was written [17]. The author wrote, "The round trip is not the part of this that matters." [18]

What to watch

  • Whether the final 9.2.0 release keeps INCREX's rc1 grammar and its SET-style NX/XX semantics unchanged.
  • A repeat of the benchmark across a real network, which would show how much the blocking pattern's penalty grows with round-trip time.
  • Whether client libraries such as redis-py expose INCREX as a first-class method, which sets how easily existing limiter code can move to it.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence55
Adoption
Insufficient
Hype gap0
Incentives
Insufficient
Confidence50
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    Across three runs each of 2,000 calls against 2,000 distinct keys, INCREX key EX 60 BYINT 1 was a repeatable 1.6x to 2x faster than the two-blocking-call pattern.

  2. [2]

    When the old pattern was pipelined instead of sent as two blocking calls, the speed gap dropped to 9-21%; the author attributes the 2x figure to the removed round trip, not to INCREX being faster on the server side.

  3. [3]

    "pipelining a conditional increment (if val == 1) is awkward enough that people skip it"

    ReportedSupportedSource: Author of the dev.to post2 sources— create a free account to open themView cited source

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · October 7, 2026

    Valkey 9.2's INCREX command removes the crash window between INCR and EXPIRE

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Loading related stories