dalli 5.0.5 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +172 -0
- data/README.md +6 -1
- data/lib/dalli/client.rb +274 -48
- data/lib/dalli/options.rb +1 -1
- data/lib/dalli/pipelined_deleter.rb +41 -17
- data/lib/dalli/pipelined_getter.rb +57 -8
- data/lib/dalli/pipelined_setter.rb +15 -3
- data/lib/dalli/protocol/base.rb +62 -5
- data/lib/dalli/protocol/connection_manager.rb +34 -10
- data/lib/dalli/protocol/key_regularizer.rb +25 -9
- data/lib/dalli/protocol/meta.rb +100 -90
- data/lib/dalli/protocol/request_formatter.rb +166 -27
- data/lib/dalli/protocol/response_buffer.rb +21 -22
- data/lib/dalli/protocol/response_processor.rb +64 -17
- data/lib/dalli/socket.rb +47 -32
- data/lib/dalli/version.rb +2 -2
- metadata +2 -3
- data/lib/dalli/pid_cache.rb +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e11a3903c6fa786ac8f175bbb95130760f3e5886dc24fdc0825abc84d743006c
|
|
4
|
+
data.tar.gz: b9b376166e61c2899f23ba4bd5367360cc14c9e10a5d21fdb3ac34558c4b7303
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bfc8526fce280bcb5003b81128b9e005975ffff54c1a7c82ce9d7b1104954c4dc169507108df70e4c5d90f444c01001640af9fccc038d180954e81f7a7459a55
|
|
7
|
+
data.tar.gz: 3c083001313d3dbdbc91cb122a76f8bd0af1fd5de75cddb89d2985ec67b40c313124ba56447818c6000d05633c80c4fae34cbaa560515a9947ca4a0fbe542134
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,178 @@ Dalli Changelog
|
|
|
4
4
|
Unreleased
|
|
5
5
|
==========
|
|
6
6
|
|
|
7
|
+
5.1.0
|
|
8
|
+
==========
|
|
9
|
+
|
|
10
|
+
Features:
|
|
11
|
+
|
|
12
|
+
- Add opaque routing tokens: `:p_token` and `:l_token` request options (#1147, #1154)
|
|
13
|
+
- `get`, `gat`, `get_cas`, `get_with_metadata`, `fetch_with_lock`, `set`/`add`/`replace`/`set_cas`/`replace_cas`, `append`/`prepend`, `incr`/`decr`, `cas`/`cas!`, `delete`/`delete_cas`, and the bulk operations (`get_multi`, `get_multi_cas`, `get_multi_with_metadata`, `set_multi`, `delete_multi`) all accept per-request `:p_token`/`:l_token` options, appended to the wire protocol as `P<token>`/`L<token>` -- applied to every key on the bulk methods
|
|
14
|
+
- memcached itself ignores these tokens; per the meta protocol spec they exist as hints for a proxy or router sitting between the client and memcached
|
|
15
|
+
- CRLF and NUL bytes raise `ArgumentError` before the request reaches the socket, both in `Dalli::Client` and in `RequestFormatter`, so a bad token can't be used for wire-protocol injection and can't close the connection out from under the caller the way a formatter-only check would
|
|
16
|
+
- The bulk methods and `delete`/`delete_cas` were deferred out of #1147 to avoid racing other in-flight PRs touching the same method signatures; #1154 completes them, including both the single-server fast path and the multi-server pipelined path for each
|
|
17
|
+
- Extracted from #1130; thanks to Nick Herson for the original idea and Jianbin Chen for porting it forward
|
|
18
|
+
|
|
19
|
+
- Support tombstone (mark-stale) deletes on `delete`, `delete_cas`, and `delete_multi` (#1145, #1153)
|
|
20
|
+
- `:invalidate` marks the item stale instead of removing it, so `#get_with_metadata` / `#get_multi_with_metadata` report `stale: true` and a reader can tell "another process is repopulating this" apart from "this was never here" -- a tombstoned key is not a miss
|
|
21
|
+
- `:tombstone_ttl` controls how long the stale marker lives; requires `:invalidate`, since memcached only honors the TTL on a delete when it accompanies the invalidate flag
|
|
22
|
+
- `:drop_value` removes the item's value but leaves the item; on its own it is not a tombstone -- reads are an ordinary hit with an empty value
|
|
23
|
+
- `delete_multi` applies the same options to every key in the batch, on both the single-server and pipelined paths; its return value keeps counting keys the server found and acted on, so under `:invalidate` it reports how many keys were tombstoned rather than removed
|
|
24
|
+
- Extracted from #1130; thanks to Jianbin Chen for this contribution
|
|
25
|
+
|
|
26
|
+
- Add `:miss` and `:return_ttl_remaining` to `get_with_metadata` (#1143)
|
|
27
|
+
- `:miss` is now always present in the returned Hash, distinguishing a true miss from a stored `nil` under `cache_nils` or a tombstoned, stale hit -- neither of which a `nil` `:value` alone can tell apart
|
|
28
|
+
- `:return_ttl_remaining` exposes the meta protocol's `t` flag as `:ttl_remaining` (seconds remaining, or `-1` for an item with no expiry), following the same opt-in shape as `:return_hit_status` / `:return_last_access`
|
|
29
|
+
- Extracted from #1130; thanks to Jianbin Chen for this contribution
|
|
30
|
+
|
|
31
|
+
- Add `get_multi_with_metadata` for stale-aware bulk reads (#1144)
|
|
32
|
+
- Returns `{ key => { value:, cas:, stale:, miss: } }` for the keys that were found; genuine misses are omitted, matching `get_multi` / `get_multi_cas` -- a tombstoned item is a hit at the protocol level, so it is still returned, with `stale: true`
|
|
33
|
+
- Routes to the same single-server fast path / pipelined-getter split as `get_multi`
|
|
34
|
+
- Extracted from #1130; thanks to Jianbin Chen for this contribution
|
|
35
|
+
|
|
36
|
+
Other changes:
|
|
37
|
+
|
|
38
|
+
- Raise the documented minimum supported memcached version to 1.6.27 (#1140)
|
|
39
|
+
- Groundwork for the features above: `drop_value` tombstone deletes require 1.6.27, the highest floor of anything landing from #1130
|
|
40
|
+
- Not enforced at runtime -- `MIN_SUPPORTED_MEMCACHED_VERSION` only gates the test harness and the README's support statement, so this changes no running client's behavior
|
|
41
|
+
|
|
42
|
+
Bug Fixes:
|
|
43
|
+
|
|
44
|
+
- Retry a transient network error during a liveness check instead of treating it as terminal (#1150)
|
|
45
|
+
- `Dalli::Protocol::Base#alive?` caught any `NetworkError` -- including `RetryableNetworkError`, a subclass -- and unconditionally converted it to `false`, with no retry. This didn't match the retry-then-raise contract every other network-facing path in Dalli follows: a single transient connection hiccup during the liveness check itself (as opposed to an actual request) permanently reported a healthy server as down for that check
|
|
46
|
+
- Now retries once on `RetryableNetworkError`, letting `error_on_request!`'s own fail-count threshold decide when to actually give up (the same mechanism the rest of the codebase relies on): it keeps retrying until `socket_max_failures` is reached, then raises a terminal `NetworkError`, which is still converted to `false` as before
|
|
47
|
+
- **Behavior change:** a server that was previously marked "down" (engaging the `down_retry_delay` cooldown) only via an actual request could now also reach that state via a liveness check (`alive?`, and anything that calls it -- `Dalli::Client#stats`, `#reset_stats`, and `Ring`'s own server selection) exhausting its retries. Previously, a solitary transient failure during a liveness check was silently forgotten rather than tracked, so the cooldown was inconsistently applied depending on which code path first observed the failure
|
|
48
|
+
- Likely a contributing cause of the same intermittently failing `get_multi` failover integration test noted in #1149: that fix addressed the send/receive phase, but the liveness-check retry it introduced can itself force a fresh `connect()` mid-retry, giving this separate, pre-existing gap in `alive?` more chances to fire
|
|
49
|
+
- Also fixed a test (`test_ring.rb`, "detect when a dead server is up again") that had been unknowingly relying on the old behavior: it never engaged the `down_retry_delay` cooldown from a single transient failure, so its 0.5s delay never actually gated anything. Updated to use a 0s delay, since the test's intent is to verify reconnection is detected, not to test cooldown timing
|
|
50
|
+
|
|
51
|
+
- Base64-encode keys containing control characters, not just NUL (#1148)
|
|
52
|
+
- `KeyRegularizer.required?` decided whether a key needed base64 encoding using `/\s/`, which matches most whitespace but none of the C0 control range (0x00-0x1F) or DEL (0x7F) -- a key that was otherwise ASCII-only and contained no whitespace (e.g. `"foo\x00bar"` or a key with an embedded ESC byte) went out on the wire unencoded
|
|
53
|
+
- Not a command-injection risk: the text protocol splits commands on CRLF, not other control bytes. The risk is key confusion -- anything downstream that treats one of these bytes specially (a C string terminating at NUL, a terminal or log line interpreting an escape byte) could silently act on a different key than Dalli believes it sent
|
|
54
|
+
- A raw control byte in a key was never protocol-compliant in the first place: memcached's own spec (`protocol.txt`) states a key "must not include control characters or whitespace." The only sanctioned way to carry such content in a key is the meta protocol's base64 (`b` flag) path -- the one whitespace and non-ASCII keys already used, and the one these keys now use too. The check is now `/[\p{Cntrl}\s]/`, matching that rule directly rather than special-casing NUL
|
|
55
|
+
- **Behavior change:** a key containing a control character now produces different bytes on the wire (base64-encoded, per the meta protocol's `b` flag) than before. Existing cache entries stored under the old, unencoded form of such a key will read as a miss once every reader has upgraded. **During a rolling deploy, old and new Dalli versions disagree about which physical key such a logical key maps to** -- not just a one-time cutover, but ongoing inconsistency between the old-version and new-version server pools for the duration of the rollout. Harmless for an ordinary cached value (worst case, extra cache misses); worth accounting for if such a key ever backs something stateful, like a lock or counter. Expected to be rare in practice: embedding a raw control byte in a cache key is unusual, and doing so was already outside what the protocol permits
|
|
56
|
+
- Found while auditing `request_formatter.rb` during the routing-token work in #1130 / #1147; unrelated to that change and predates it
|
|
57
|
+
|
|
58
|
+
- Retry transient network errors in `get_multi`, `set_multi` and `delete_multi` instead of silently swallowing them (#1149)
|
|
59
|
+
- All three methods group keys by server and issue one request per server. Each per-server rescue clause caught `DalliError` and `NetworkError` together and swallowed both, just debug-logging -- since `RetryableNetworkError < NetworkError`, this also silently swallowed transient, retryable failures, dropping that server's keys from the result instead of the whole operation retrying (`get_multi`/`set_multi`'s single-server fast path did not even attempt a retry, on any failure)
|
|
60
|
+
- Six rescue sites across `PipelinedGetter`, `PipelinedSetter`, `PipelinedDeleter` and the `single_server_*` fast paths now retry a transient `RetryableNetworkError`, matching sibling rescue sites in the same files that already did this correctly
|
|
61
|
+
- **Behavior change:** if a server remains unreachable after retrying (not just a transient blip), these three methods now raise `Dalli::NetworkError` instead of silently returning an incomplete or empty result. This matches how every other Dalli::Client method already behaves on a hard network failure, and is the retry-then-raise behavior `delete_multi`'s own docs already described; it was just not reliably true for this failure path before. Code that calls these methods without rescuing `Dalli::NetworkError` should account for this if it can reach a fully unreachable server
|
|
62
|
+
- Likely the cause of an intermittently failing `get_multi` failover integration test that recurred across multiple PRs, though this could not be directly confirmed: the failure (an empty result with no exception in the logs) never reproduced locally despite repeated attempts, consistent with needing a genuine transient network hiccup that is far more likely on a loaded CI runner than an idle dev machine
|
|
63
|
+
|
|
64
|
+
5.0.6
|
|
65
|
+
==========
|
|
66
|
+
|
|
67
|
+
Performance:
|
|
68
|
+
|
|
69
|
+
- Skip the cas-return flag on quiet `meta_set` requests (#1131)
|
|
70
|
+
- In quiet mode memcached suppresses the `ms` response entirely, so the CAS requested by the `c` flag can never be read; sending it only added two bytes to every request
|
|
71
|
+
- Applies to the bulk-write paths, where quiet sets are emitted: `Dalli::Client#multi` blocks and the pipelined setter
|
|
72
|
+
- Extracted from #1130; thanks to Jianbin Chen for this contribution
|
|
73
|
+
|
|
74
|
+
- Reduce allocations in `KeyRegularizer` and multi-key request paths (#1120)
|
|
75
|
+
- Decomposed `KeyRegularizer#encode` into separate `needs_encoding?` and `encode` calls so the common happy path avoids allocating an intermediate array for the two-element return value
|
|
76
|
+
- Refactored `multi_get`/`multi_set`/`multi_delete` command generation into `RequestFormatter` to share its key-encoding helpers
|
|
77
|
+
- Thanks to Jean Boussier for this contribution
|
|
78
|
+
|
|
79
|
+
- Reduce allocations in `ResponseBuffer` pipelined getk parsing (#1117)
|
|
80
|
+
- `process_single_getk_response` was building a fresh array to return results alongside the updated offset; refactored to store the offset as the last element of the existing tokens array and pop it, saving one allocation per response
|
|
81
|
+
- Also skips trailing nils in the token array
|
|
82
|
+
- Thanks to Jean Boussier for this contribution
|
|
83
|
+
|
|
84
|
+
- Enable frozen string literals in `RequestFormatter` (#1118)
|
|
85
|
+
- Frozen string literals had been inadvertently disabled; re-enabling reduces allocations by ~300,000 objects in a 10,000-iteration `get_multi_cas` benchmark (562 MB → 550 MB total allocated)
|
|
86
|
+
- Thanks to Jean Boussier for this contribution
|
|
87
|
+
|
|
88
|
+
- Reduce allocations in `ResponseProcessor#value_from_tokens` (#1113)
|
|
89
|
+
- `token[1..].to_i` was allocating a new string for every token parsed; replaced with in-place `slice!` followed by a token reset to avoid poisoning subsequent token comparisons
|
|
90
|
+
- Saves 4 allocations per entry in `get_multi_cas` workloads (a hotspot for IdentityCache)
|
|
91
|
+
- Thanks to Jean Boussier for this contribution
|
|
92
|
+
|
|
93
|
+
- Reduce allocations in common operation paths (#1111)
|
|
94
|
+
- Use `Symbol#name` over `Symbol#to_s` to return a frozen string without allocation
|
|
95
|
+
- Skip trace attribute hash construction when OpenTelemetry instrumentation is disabled
|
|
96
|
+
- Use argument forwarding (`...`) in `Client#perform` and `Threadsafe#request` to avoid splat array allocation
|
|
97
|
+
- Use `match?` in `KeyRegularizer#encode` to avoid `MatchData` object allocation
|
|
98
|
+
- Reduces objects allocated by ~26% and memory by ~6% for a simple `get` workload
|
|
99
|
+
- Thanks to Jean Boussier for this contribution
|
|
100
|
+
|
|
101
|
+
- Fix pathological memory behavior in `ResponseBuffer` (#1114)
|
|
102
|
+
- `compact_if_needed` was intended to reclaim memory by slicing off consumed bytes, but `buffer.byteslice(@offset..)` on an unfrozen string causes Ruby to allocate a hidden third string as the copy-on-write owner rather than freeing the original
|
|
103
|
+
- Redesigns buffer management to pass reusable buffer objects directly to `read`/`read_nonblock`, avoiding reallocation on each response read
|
|
104
|
+
- Reduces allocations from ~2.38 GB to ~649 MB in a `get_multi_cas` benchmark over 10,000 iterations
|
|
105
|
+
- Accompanied by new unit tests for `ResponseBuffer` (#1115)
|
|
106
|
+
- Thanks to Jean Boussier for this contribution
|
|
107
|
+
|
|
108
|
+
Features:
|
|
109
|
+
|
|
110
|
+
- `delete_multi` now returns the number of keys found and deleted (#1126)
|
|
111
|
+
- Previously the return value was unspecified; callers (e.g. Rails, see rails/rails#58071) had no way to tell how many keys were actually removed
|
|
112
|
+
- The count is derived from the meta protocol's quiet-mode delete responses with no extra round-trips: successful deletes are suppressed while misses report `NF`, so any response received before the terminator is a key that was not deleted
|
|
113
|
+
- The single-server fast path now shares the pipelined path's bounded retry on transient (`RetryableNetworkError`) network errors, so both paths behave consistently; the returned count is best-effort and may under-report if a network error triggers a retry, since keys deleted before the error are not recounted
|
|
114
|
+
- Thanks to Iliana Hadzhiatanasova for this contribution
|
|
115
|
+
|
|
116
|
+
Bug Fixes:
|
|
117
|
+
|
|
118
|
+
- Raise instead of returning a truncated value when the peer closes mid-response (#1135)
|
|
119
|
+
- `IO#read(count)` on a blocking socket accumulates across TCP chunks and hands back a shorter buffer (or `nil`) in only one case: the stream hit EOF. That short buffer was passed through as the response body, so a memcached restart, proxy drop, or load balancer timeout partway through a response could surface a truncated but still decodable value to the caller, indistinguishable from a real one
|
|
120
|
+
- A short read is now treated as the premature EOF it is, raising and closing the dirty socket so the request is retried on a fresh connection
|
|
121
|
+
- CRuby only; the JRuby path already used `Socket#readfull`, which enforces the same contract
|
|
122
|
+
- Extracted from #1130; thanks to Ian Ker-Seymer for the original fix and Jianbin Chen for the port
|
|
123
|
+
|
|
124
|
+
- Tear down the connection when a non-`StandardError` aborts a request (#1136)
|
|
125
|
+
- `Async::Stop` and `Thread#kill` descend from `Exception` rather than `StandardError`, so the rescue clauses in `Protocol::Base#request` never saw them; a scheduler cancelling a fiber parked on a response read skipped `close` entirely, leaving the connection marked as having a request in progress with partial response bytes still unread on the wire, and returning that half-used client to the pool under `connection_pool`
|
|
126
|
+
- `Protocol::Base#request` now closes in an `ensure` unless the request ran to completion, and `ConnectionManager#close` performs its state cleanup in an `ensure` so a second cancellation landing inside `@sock.close` cannot leave the socket non-nil with the request still marked in progress
|
|
127
|
+
- `Dalli::DalliError` and `Dalli::MarshalError` now close the connection at the point of failure rather than at the start of the next request; those paths already left the request in progress and `ConnectionManager#confirm_ready!` closed on the next call, so this changes when the close happens rather than adding one
|
|
128
|
+
- Extracted from #1130; thanks to Dan Mayer for the original fix and Jianbin Chen for the port
|
|
129
|
+
|
|
130
|
+
- Fix `ResponseBuffer` compaction logic (#1119)
|
|
131
|
+
- `COMPACT_THRESHOLD` was removed in #1116 as apparently unused, but the constant was referenced by the compaction guard; its absence silently disabled buffer compaction
|
|
132
|
+
- Restored the constant, corrected the compaction condition, and improved the buffer-shrinking implementation to use `String#bytesplice` (backed by `memmove`) for true in-place compaction
|
|
133
|
+
- Adds targeted tests covering the compaction threshold and shrink behavior
|
|
134
|
+
- Thanks to Jean Boussier for this contribution
|
|
135
|
+
|
|
136
|
+
Maintenance:
|
|
137
|
+
|
|
138
|
+
- Scope `StrictWarnings` to Dalli's own source (#1134)
|
|
139
|
+
- The test suite runs under `-w` and prepends a hook to `Warning.singleton_class` that turns warnings into failures, but that hook is global: a warning emitted while loading any third-party gem aborted the whole suite before a single test ran
|
|
140
|
+
- `json` 2.21.2's pure-Ruby generator (used on JRuby, where the C extension is unavailable) warns `method redefined; discarding old to_hash` at require time, which took the `jruby-10` CI job red with no change to Dalli
|
|
141
|
+
- Warnings are now attributed to a source file and only raise for `lib/` and `test/`; attribution prefers the location Ruby embeds in the message, since the stack at that point describes the require chain rather than the offending code
|
|
142
|
+
- Portable attribution of `Kernel#warn` callers also required walking the stack rather than indexing it (Ruby 3.3/3.4 push an `<internal:warning>` frame that 4.0 does not), skipping RubyGems' `Kernel#warn` shim (active on JRuby but not CRuby), and resolving relative backtrace paths
|
|
143
|
+
|
|
144
|
+
- Make raw and namespace fast path tests actually use those options (#1129)
|
|
145
|
+
- Followup to #1127: the `raw` and `namespace` variants passed those options to the helper that starts memcached, which configures the client the tests then discarded, so neither option was ever exercised
|
|
146
|
+
- Passes the options to the client under test and adds assertions that fail if they are absent
|
|
147
|
+
- Thanks to Iliana Hadzhiatanasova for this contribution
|
|
148
|
+
|
|
149
|
+
- Benchmark `set_multi` and add a `delete_multi` target (#1132)
|
|
150
|
+
- Enables the two `set_multi` reports that were commented out pending the arrival of `set_multi`, resolving the accompanying TODO
|
|
151
|
+
- Adds a `delete_multi` target comparing the pipelined path against N single deletes
|
|
152
|
+
- Extracted from #1130; thanks to Jianbin Chen for this contribution
|
|
153
|
+
|
|
154
|
+
- Bump CI memcached to 1.6.41 and run benchmarks on pull requests (#1133)
|
|
155
|
+
- The tests workflow moves from 1.6.40 to 1.6.41; the benchmarks and profile workflows had drifted back on 1.6.23
|
|
156
|
+
- Extracted from #1130; thanks to Jianbin Chen for this contribution
|
|
157
|
+
|
|
158
|
+
- Disable RuboCop metrics cops (#1128)
|
|
159
|
+
- Thanks to Jean Boussier for this contribution
|
|
160
|
+
|
|
161
|
+
- Remove `PIDCache` module (#1125)
|
|
162
|
+
- `Process.pid` is cached natively by Ruby 3.3+ (via https://bugs.ruby-lang.org/issues/19443), making the manual cache unnecessary now that Dalli requires Ruby 3.3+
|
|
163
|
+
- Thanks to Jean Boussier for this contribution
|
|
164
|
+
|
|
165
|
+
- Remove unused `COMPACT_THRESHOLD` constant from `ResponseBuffer` (#1116)
|
|
166
|
+
- Followup cleanup after the buffer management redesign in #1114
|
|
167
|
+
- Note: subsequently found to be in use; restored and corrected in #1119
|
|
168
|
+
- Thanks to Jean Boussier for this contribution
|
|
169
|
+
|
|
170
|
+
- Use `String#byteindex` instead of `String#index` when searching for the response terminator in `getk_response_from_buffer` (#1112)
|
|
171
|
+
- The result feeds directly into `byteslice`; `byteindex` makes the intent explicit, though both return the same value since the buffer encoding is always `BINARY`
|
|
172
|
+
- Thanks to Jean Boussier for this contribution
|
|
173
|
+
|
|
174
|
+
- Make single-server fast path tests actually exercise the fast path (#1127)
|
|
175
|
+
- The batch-operation tests built clients through a helper that registers two address aliases for the same memcached process, so every client had a 2-server ring and the tests always ran through the pipelined path instead of the single-server fast path
|
|
176
|
+
- Adds a `single_server_client` test helper that builds a client with a single address, and uses it in the affected `get_multi`, `set_multi`, and `delete_multi` tests
|
|
177
|
+
- Thanks to Iliana Hadzhiatanasova for this contribution
|
|
178
|
+
|
|
7
179
|
5.0.5
|
|
8
180
|
==========
|
|
9
181
|
|
data/README.md
CHANGED
|
@@ -17,7 +17,12 @@ The name is a variant of Salvador Dali for his famous painting [The Persistence
|
|
|
17
17
|
## Requirements
|
|
18
18
|
|
|
19
19
|
* Ruby 3.3 or later (JRuby also supported)
|
|
20
|
-
* memcached 1.6 or later
|
|
20
|
+
* memcached 1.6.27 or later
|
|
21
|
+
|
|
22
|
+
Dalli is tested against both the minimum supported memcached version and the
|
|
23
|
+
latest release. Earlier 1.6.x servers are not supported: the meta protocol
|
|
24
|
+
rejects unknown flags outright, so features added after a server's release fail
|
|
25
|
+
with `CLIENT_ERROR invalid flag` rather than degrading.
|
|
21
26
|
|
|
22
27
|
## Configuration Options
|
|
23
28
|
|