May 9, 2025·4 min read·engineeringdragonflypipeliningmemcachedjson

Dragonfly 1.30 changes pipeline scheduling and adds GETS replies

Dragonfly 1.30 batches connection work more effectively and adds Memcached GETS with a placeholder token. JSON fixes bound nesting and separate recursive traversal from mutation.

Yielding after a fixed number of requests can interrupt cheap operations more often than their CPU cost warrants. Dragonfly's pipeline scheduling change replaces that approach with a time-based budget for reading and parsing socket input. It also increases the read-buffer capacity available for processing requests in bulk. Those changes ship in Dragonfly 1.30 on May 9.

The release adds Memcached GETS support and several JSON stability fixes. GETS has a qualification that belongs near the headline. Its implementation always returns a placeholder CAS token of zero. Accepting the command and formatting its response therefore does not establish full compare-and-swap semantics.

I find Dragonfly useful when a cache needs more of one machine's capacity. Pipelining is relevant to that choice because it changes how much work the process can receive before waiting on another client round trip. Its implementation still has to share CPU fairly and preserve each protocol's response behavior.

CPU time determines when parsing yields

The pipeline patch introduces max_busy_read_usec, initially set to 100 microseconds, to describe how long socket reading and parsing can continue without yielding. It removes the older streak-based logic that yielded after a count of asynchronously dispatched items. A time budget relates the scheduling decision to the work performed instead of assuming every request costs the same.

That distinction matters for pipelines containing many small requests. Frequent switching can add overhead before the parser has produced a useful batch. Conversely, letting a connection continue indefinitely can delay other work. The patch gives the read-and-parse path an explicit unit for deciding when to cooperate with the scheduler.

The reported measurements include a particular pipeline range and an experimental sharding configuration. They should not be turned into a universal latency prediction. The mechanism supports the narrower claim that the connection path can process input in larger useful groups while limiting uninterrupted parsing time.

Batching also has an allocation cost

The related MGET and squashing change removes a separate vector by keeping a dispatched command and its captured reply together. It also stops deduplicating MGET keys by default, while retaining a runtime option. Both changes reduce preparation around the actual lookups, although their value depends on the request pattern.

Deduplication itself performs work. It can avoid repeated lookups when requests contain repeated keys, but constructing that intermediate representation costs allocations or bookkeeping. The default changes the balance for the common path rather than proving repeated keys are impossible. The retained option records that there can be a different tradeoff for other workloads.

The patch also notes that the asynchronous-dispatch guard must cover reply flushing. Guarding writes into a reply builder while allowing an unguarded flush would leave another way to access shared output state concurrently. Scheduling, storage of captured replies and flushing all need to agree on who owns the output path.

GETS supplies a compatible response shape

Memcached GETS asks for a value response containing a CAS token in addition to the usual fields. Dragonfly's new handler routes it through its multi-get path and sets a flag requesting token output. The reply builder now uses an explicit choice to include that field, which is necessary even when the token's numeric value is zero.

The regression test checks a response containing that zero token and distinguishes it from the ordinary GET response, which omits the field. This is a protocol-format improvement for clients that issue GETS. It does not provide a changing version identifier which an application can use to infer that no intervening write occurred.

That limit is more useful than an unqualified compatibility claim. A client which uses GETS only as a retrieval form can benefit from the added response support. A workflow which depends on version-sensitive conditional updates has a stronger requirement and cannot derive it from the presence of the extra numeric field.

TLS startup validation for Memcached also changes. The server can use TLS with its Memcached listener without requiring the password setting expected by the previous validation. That separates transport encryption from a password-authentication mechanism which this command path does not supply.

Deep JSON input has an explicit limit

The JSON nesting correction lowers the accepted nesting limit from 256 to 64. Its explanation describes excessive resource consumption with deeply nested test data and says a limit of 128 did not resolve that case. The regression expects a parsing error for input beyond the supported depth.

This is a behavior change with a visible input boundary. A syntactically valid JSON document can still exceed the server's supported nesting depth. Applications storing generated or externally supplied documents need to interpret that rejection as a supported resource limit, rather than assuming every valid JSON structure is accepted.

The recursive JSON.SET repair addresses another form of unbounded work. It collects distinct mutation targets during traversal and applies the changes afterward. Mutating the structure while recursively discovering matching nodes can otherwise change what the ongoing traversal sees.

Separating discovery from mutation gives the operation a stable set of targets. The patch uses both an ordered collection and a uniqueness check, so repeated discovery does not create duplicate mutations. Its tests cover replacing recursively selected values with objects which themselves contain a matching field name. That case distinguishes the original targets from nodes introduced by the replacement. Without the distinction, an operation can keep discovering work created by its own modifications instead of completing the requested update.

I regard that combination as a useful account of the release. The pipeline work removes avoidable scheduling and allocation overhead, while the JSON changes bound and stabilize operations on structured values. Dragonfly 1.30 adds a specific Memcached retrieval response, with its placeholder token stated explicitly, alongside changes to how existing requests progress through the engine.