# Redis Durability, Reconsidered: How Invar Closes the WAIT Gap

## Overview

[Invar](https://github.com/hardpointlabs/invar) is a diskless document store that speaks the Redis™ wire protocol. Unlike Redis or its spiritual descendants such as [ValKey](https://valkey.io), [KeyDB](https://docs.keydb.dev) or even [DragonflyDB](https://www.dragonflydb.io/), it doesn't primarily aim to be an in-memory db, and durability is one of the places where that contrast is most stark.

Redis was built around an in-memory data structure server that later grew optional persistence; Invar was built the other way around, with an LSM engine (SlateDB) that treats object storage as the source of truth from day one, backed by S3's own strong consistency guarantees. That different starting point produces fundamentally different durability properties, not just different defaults. Here's what that actually means in practice.

## How Redis persists data

Redis gives you two persistence mechanisms, and every real deployment is a trade-off between them:

1.  **RDB** takes periodic point-in-time snapshots of the whole dataset: fork the process, write the snapshot, done. It's cheap and fast, but anything written between the last snapshot and a crash is gone. A snapshot every few minutes means you can lose a few minutes of writes.
    
2.  **AOF** (append-only file) logs every write command as it happens, and its durability is governed by whatever you configure via `appendfsync`:
    
    *   `always` fsyncs on every write. This is the safest, but also slowest: every write pays a disk round trip
        
    *   `everysec` fsyncs once a second (the common default). This gives you much better throughput, but also a bounded time window of potential data loss
        
    *   `no` leaves fsync timing to the OS (fastest, least safe)
        

Both are useful mechanisms, but they share a weakness: durability is a *rate* you tune, traded directly against throughput, and the loss window is a matter of "how much you can tolerate," not "whether you can tolerate it".

## How Invar persists data

Invar's writes go through [SlateDB](https://slatedb.io/)'s write-ahead log, flushed to object storage on a timer (`flush_interval`, defaulting to 100ms). That much sounds similar to AOF's `everysec` - a periodic, rate-based sync with persistent storage. The part that's different is what "flushed" actually means once it lands.

When SlateDB confirms a write is durable, that confirmation is backed by S3's own [consistency model](https://aws.amazon.com/s3/consistency/), and AWS's own documentation is specific about what that guarantees:

> *"after a successful write of a new object, or an overwrite or delete of an existing object, any subsequent read request immediately receives the latest version of the object."*

Strongly consistent PUT, GET, and LIST, unconditionally, since December 2020, in every region. Once a write is acknowledged durable, there's no replica that might still be catching up, or some time window where a reader could see stale data; the object store itself won't serve anything else.

SlateDB exposes this as a per-write choice rather than a global setting: writes can opt into blocking until durable (`await_durable`), or return fast and rely on the next scheduled flush. And critically, durability tracking is monotonic: a single sequence number (`durable_seq`) that only advances, rather than something that has to be reconstructed per-operation after the fact. SlateDB can build this

Since many architectural diagrams compress the inner workings of S3 into a single box, it's easy to forget that it's a complex, sprawling distributed system, but our ability to lean on its guarantees means we don't have to build the clustering machinery that Redis has spent over a decade

## How Invar handles WAIT, SAVE & BGSAVE

Redis exposes two main sets of commands for persisting writes:

`SAVE`**/**`BGSAVE` are whole-dataset, administrative operations: checkpoint everything, right now, for backup or migration purposes. In regular Redis, this takes a point-in-time snapshot of the data in the Redis process and writes it to an RDB file. This is potentially quite an expensive operation and Redis' [own docs](https://redis.io/docs/latest/commands/save/) warn about performing this in hot-path production code. Calling `SAVE` or `BGSAVE` in Invar effectively triggers an on-demand WAL flush rather than waiting for SlateDB's own `flush_interval` to tick over. So while this potentially results in additional PUTs to object storage, it's won't freeze your application unless you've somehow accumulated a huge amount of outstanding modifications since `flush_interval` last rolled around.

`WAIT` is the *client-facing* durability primitive, and it's the one Invar's durability story is actually built on. In Invar, `WAIT` maps onto exactly the mechanism above: each connection tracks the sequence number of its most recent write, then blocks until `durable_seq` catches up, or the timeout elapses, honoring the same semantics of Redis's own `WAIT`. The only bookkeeping needed on Invar's side is to track per-connection sequence numbers: confirming the latest write is durable inherently confirms everything before it (remember, `durable_seq` is monotonic). If a client-issued `WAIT` blocks or comes back with `1`, a client can be assured that its writes are durable against death of the Invar process; no second-guessing if your writes are 'safe enough' based on how many replicas they've propagated to; with Invar it's a simple binary choice: either your writes are confirmed safe, or they're not. Another mundane difference in Invar's `WAIT` implementation is that the `numreplicas` argument is ignored; we consider that S3 is the sole replica a single Invar instance will ever hold, and if you get a `1`, you can assume you're good to go.

## What "strong consistency" actually requires

To really claim strong consistency, you need 2 things, whether you choose Redis or Invar:

*   Synchronous replication to a majority of nodes
    
*   A failover mechanism that guarantees only a replica holding the complete acknowledged history can ever be promoted to leader
    

In Redis, `WAIT` delivers the first point (as long as you check the resulting replication count!) but neither Redis alone, nor Cluster/Sentinel can offer a failover that *guarantees* the next leader will have a faithful copy of the current DB state, only one that has the *greatest amount of data* . The practical result: `WAIT` makes a lost write *less likely* during failover, by ensuring more replicas hold a copy before a leader change happens, but it doesn't make loss impossible. Salvatore Sanfilippo's own SO answer [puts it plainly](https://stackoverflow.com/questions/33629339/can-the-wait-command-provide-strong-consistency-in-redis): `WAIT` does not make Redis linearizable.

That's a gap which only becomes a problem worth solving once you've introduced multiple nodes in the first place: a single Redis instance has no replicas to synchronize with, so even this partial protection is only available once you've taken on the operational weight of a cluster or Sentinel deployment.

Invar sidesteps the problem differently by enforcing a single process, single writer design, and delegating consistency to S3's own read-after-write model instead. For the avoidance of doubt, Invar also doesn't claim to support true "[linearizable](https://en.wikipedia.org/wiki/Linearizability)" consistency either: we can't, since AWS doesn't claim this for S3; their own documentation is careful to say "strong read-after-write consistency," which is more specific.

The difference is that Invar piggybacks off SlateDB's [fencing protocol](https://slatedb.io/rfcs/0001-manifest/) guarantees, i.e. that there's only ever one legitimate writer in the first place, which is exactly the failover mechanism Redis Cluster/Sentinel lacks. Removing the possibility of multiple writers elides a whole class of problems that don't need solving after the fact.

## Invar's consistency model

I've focused on *durability* in this post; consistency deserves its own more thorough exploration. But for now I'll summarize where we're at:

Invar currently uses Snapshot Isolation (SI) for all writes. That's not a hidden limitation; it's the default a lot of production databases ship with (PostgreSQL's `REPEATABLE READ` is SI too), and it's a great fit for the overwhelming majority of workloads.

SlateDB itself already supports more than this. It can be configured for SSI, detecting read-write conflicts (write skew) that plain Snapshot Isolation doesn't catch, and Invar's strict single-writer model means it can support a lighter-weight option, independently of the storage engine: fully serialized, single-threaded execution, closer to how Redis itself behaves.

Exposing these as tunable choices (likely per key prefix), so a handful of genuinely hot, correctness-sensitive keys can opt into stronger guarantees without taxing everything else, is on the roadmap.

## Try it out!

If you want solid Redis durability for your Redis data but don't want to run a cluster, give Invar a try!

*   [Download & run it yourself](https://docs.hardpoint.dev/guides/invar/quick-start)
    
*   [Join the waitlist](https://accounts.hardpoint.dev/waitlist) for our managed cloud solution
