Dragonfly 1.1 fixes hash conversion and transaction ordering
Dragonfly 1.1 fixes a shared conversion buffer and an ARM64 memory-ordering bug. RESP3 negotiation and authenticated replication extend compatibility in the same release.
A field name and a field value can each be correct when decoded separately and still become corrupt when code holds views into the same temporary buffer. Dragonfly's hash-conversion fix in pull request 994 addresses that specific ownership error. It is one reason the April 2 release of Dragonfly 1.1 recommends an upgrade for production users of 1.0.
The release also repairs a transaction memory-ordering bug associated with ARM64 crashes. These fixes concern two different ways of making intermediate results visible. One requires distinct storage for decoded data. The other requires a synchronization relationship between worker threads and their coordinator. Both failures occur beneath commands whose external syntax remains unchanged.
I pay attention to this part of Dragonfly because its attraction is a multithreaded engine behind familiar Redis interfaces. Familiar commands reduce adoption work, but the engine still has to preserve the lifetime and visibility of the data they return. These patches provide unusually concrete explanations of the implementation work behind that compatibility.
A view does not preserve its backing bytes
The hash patch changes ConvertToStrMap in the hash-command implementation. The function traverses an existing listpack and converts its alternating field and value entries into a string map. It calls LpGetView for the field, advances to the value, calls the helper again, and inserts the resulting pair.
Before the fix, both calls received the same integer-conversion buffer. A string view records a location and a length. It does not acquire an independent copy of those bytes merely because the caller gives the view a variable name. If decoding the next entry writes into that buffer, a previous view can observe the replacement bytes.
The patch replaces the single buffer with two buffers and gives one to each decoding call. The field view and the value view consequently have separate temporary storage while the map insertion consumes them. This is a small code change with a specific lifetime argument. Adding more copying throughout the hash implementation is unnecessary when the conflict is between these two adjacent conversions.
The new regression test inserts two hundred numeric-looking fields with a numeric-looking value. Its comment says this is enough to make the listpack unsuitable and trigger conversion. The assertion checks the resulting hash length. That choice targets the representation transition where shared temporary storage can affect fields, rather than exercising only the steady-state lookup path.
The release notes describe the symptom as corruption associated with HGET, while the patch is explicitly a hash buffer fix during conversion. Those descriptions refer to different levels of the problem. A caller can discover incorrect hash contents during a read even when the faulty operation happened earlier as the container changed representation.
This distinction matters to diagnosis. Repeating a read against a small, unchanged hash would not necessarily exercise the conversion function. The regression needs enough entries to change the internal representation, and its numeric-looking inputs engage the temporary conversion buffers. The source gives a narrower and more useful explanation than treating every hash read as equally involved.
Completion needs a memory-ordering relationship
The transaction fix in pull request 1009 changes how the coordinator observes worker completion. The workers decrement a shared run count after doing their work. Its coordinator waits until that count reaches zero and then uses results produced by the workers.
The decrement already uses release memory ordering. The patch explains that worker stores must become visible after the coordinator observes completion. A notification mechanism alone does not establish the required relationship on every path because the waiting code can find the counter at zero and skip reading the event mechanism.
The fix adds an acquire fence after the counter wait. Its comment connects that fence to the release decrement. The intended relationship therefore holds even when the wait finishes by observing the counter directly. Seeing that the workers have finished and safely observing everything they wrote require the corresponding ordering, not just a numerical comparison.
The patch also changes the callback representation to a function reference and stores a pointer to the callback during execution. Several assertions and resets change with it. These edits belong to the same coordination implementation, but the explicit acquire fence is the part the source identifies as fixing coordinator reads.
The release calls out ARM64 because the bug can cause crashes there. The patch's explanation is expressed through C++ synchronization operations rather than a special case that adds locking only for one architecture. That is the useful engineering lesson in the source. The validity of the handoff belongs to the memory model used by both producer and consumer.
A shared-nothing dictionary layout does not remove all cross-thread relationships. Command coordination still transfers completion and results between workers. This patch shows where that relationship exists in Dragonfly's transaction code. The internal shards can own separate data and still need a precise synchronization boundary when a command finishes.
RESP3 starts with connection negotiation
Version 1.1 also adds RESP3. The release-tagged server implementation makes the negotiation behaviour explicit in ServerFamily::Hello. It accepts protocol versions two and three and sets the reply builder's protocol state accordingly.
The relevant exchange can be inspected using a RESP3-capable Redis command-line client:
redis-cli -3 HELLO 3
The server returns fields identifying the protocol, client connection, server versions, mode, and role. In this implementation the response includes both the compatibility version and a separate dfly_version field. That lets a connection expose Redis-compatible identity information while also identifying the Dragonfly build.
Negotiation belongs to the connection. The handler permits HELLO 2 to switch back to RESP2, and calling HELLO without an argument selects RESP2. Applications do not obtain RESP3 solely because the binary has been upgraded. Their clients need to request the protocol.
There is also an explicit limit in this version. The handler does not support the AUTH and SETNAME options on HELLO. It rejects extra arguments even when the protocol number itself is valid. A client that combines authentication with protocol negotiation therefore exercises a different path from a client that authenticates separately and then sends HELLO 3.
I regard these connection details as more useful than a binary statement that a client supports RESP3. A library's startup sequence can matter as much as its response parser. The tagged handler gives enough information to distinguish protocol support from support for every optional negotiation form.
Replicas authenticate on their own connections
The authenticated-replication patch introduces the masterauth flag. Its purpose is to let a replica supply the password required by its primary. This is separate from the password that clients use to connect to the replica itself.
The added test makes that separation explicit. It creates the primary with one requirepass value and the replica with a different requirepass value. The replica's masterauth matches the primary's password. Test clients then authenticate to each process using the password appropriate for that process.
The new ConnectAndAuth helper connects the socket and, when configured, sends AUTH before continuing the replication greeting. It checks for an OK response. Initial startup and the reconnect loop both use the helper, so the change covers the lifetime of the connection beyond its first successful establishment.
Dragonfly also opens replication flow connections, and the patch uses the same authentication helper when starting a full-sync flow. That detail matters because protecting the primary affects every relevant connection, not merely the initial control exchange. A successful authenticated greeting on one socket does not authenticate another socket automatically.
The test populates data on the primary and reads the replicated keys from the replica after waiting for replication execution. This connects authentication to the subsequent data path. The purpose is to demonstrate that password protection and replication work together with distinct client credentials on the two processes.
The release changes correctness before application syntax
These fixes address ordinary operations and internal transitions that applications already depend on. Hash conversion repairs the storage backing decoded fields. Transaction ordering repairs visibility when workers report completion. Neither fix requires an application to adopt a new command to benefit from it.
The protocol and replication additions then extend the interactions available around that engine. RESP3 negotiation changes the selected connection protocol, while masterauth permits replication to a password-protected primary. The details in their handlers explain which startup exchanges this release supports and where its compatibility still has explicit limits.