dalli 5.0.1 → 5.0.2
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 +14 -0
- data/lib/dalli/client.rb +53 -6
- data/lib/dalli/protocol/meta.rb +76 -0
- data/lib/dalli/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae3cbae2603955279bb78b75682ac1f56105d5ec2b2e0d464ab15255ed23369b
|
|
4
|
+
data.tar.gz: 2e5a3960e638293cfdf5663ef959d1ab9690620e09f0151a844cdeb9424cbf80
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 243382482bc345bf982f2cb96fae28b974f37b2eee653a71c7f3a85518ef5acdbfda6f2fddacfa31da6252774000e11a1c96d3707d29808c636951d85ace17be
|
|
7
|
+
data.tar.gz: 5938608c26cd307e397cad4fdf6e2fda4519fdbd8df2b7d75352b691996fec4dca59632cc0053b8bf410612c8e6cada73ccad7baf5c000a183bd04bb196d056e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
Dalli Changelog
|
|
2
2
|
=====================
|
|
3
3
|
|
|
4
|
+
5.0.2
|
|
5
|
+
==========
|
|
6
|
+
|
|
7
|
+
Performance:
|
|
8
|
+
|
|
9
|
+
- Add single-server fast path for `get_multi`, `set_multi`, and `delete_multi` (#1077)
|
|
10
|
+
- When only one memcached server is configured, bypass the `Pipelined*` machinery (IO.select, response buffering, server grouping) and issue all quiet meta requests inline followed by a noop terminator
|
|
11
|
+
- `get_multi` shows ~1.5x improvement at 10 keys and ~1.75x at 100–500 keys compared to the `PipelinedGetter` path
|
|
12
|
+
- Thanks to Dan Mayer (Shopify) for this contribution
|
|
13
|
+
|
|
14
|
+
Development:
|
|
15
|
+
|
|
16
|
+
- Add `bin/benchmark_branch` script for benchmarking against the current branch
|
|
17
|
+
|
|
4
18
|
5.0.1
|
|
5
19
|
==========
|
|
6
20
|
|
data/lib/dalli/client.rb
CHANGED
|
@@ -311,7 +311,11 @@ module Dalli
|
|
|
311
311
|
return if hash.empty?
|
|
312
312
|
|
|
313
313
|
Instrumentation.trace('set_multi', multi_trace_attrs('set_multi', hash.size, hash.keys)) do
|
|
314
|
-
|
|
314
|
+
if ring.servers.size == 1
|
|
315
|
+
single_server_set_multi(hash, ttl_or_default(ttl), req_options)
|
|
316
|
+
else
|
|
317
|
+
pipelined_setter.process(hash, ttl_or_default(ttl), req_options)
|
|
318
|
+
end
|
|
315
319
|
end
|
|
316
320
|
end
|
|
317
321
|
|
|
@@ -368,7 +372,11 @@ module Dalli
|
|
|
368
372
|
return if keys.empty?
|
|
369
373
|
|
|
370
374
|
Instrumentation.trace('delete_multi', multi_trace_attrs('delete_multi', keys.size, keys)) do
|
|
371
|
-
|
|
375
|
+
if ring.servers.size == 1
|
|
376
|
+
single_server_delete_multi(keys)
|
|
377
|
+
else
|
|
378
|
+
pipelined_deleter.process(keys)
|
|
379
|
+
end
|
|
372
380
|
end
|
|
373
381
|
end
|
|
374
382
|
|
|
@@ -522,13 +530,52 @@ module Dalli
|
|
|
522
530
|
|
|
523
531
|
def get_multi_hash(keys)
|
|
524
532
|
Instrumentation.trace_with_result('get_multi', get_multi_attributes(keys)) do |span|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
533
|
+
hash = if ring.servers.size == 1
|
|
534
|
+
single_server_get_multi(keys)
|
|
535
|
+
else
|
|
536
|
+
{}.tap do |h|
|
|
537
|
+
pipelined_getter.process(keys) { |k, data| h[k] = data.first }
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
record_hit_miss_metrics(span, keys.size, hash.size)
|
|
541
|
+
hash
|
|
529
542
|
end
|
|
530
543
|
end
|
|
531
544
|
|
|
545
|
+
def single_server
|
|
546
|
+
server = ring.servers.first
|
|
547
|
+
server if server&.alive?
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def single_server_get_multi(keys)
|
|
551
|
+
keys.map! { |k| @key_manager.validate_key(k.to_s) }
|
|
552
|
+
return {} unless (server = single_server)
|
|
553
|
+
|
|
554
|
+
result = server.request(:read_multi_req, keys)
|
|
555
|
+
result.transform_keys! { |k| @key_manager.key_without_namespace(k) }
|
|
556
|
+
result
|
|
557
|
+
rescue Dalli::NetworkError
|
|
558
|
+
{}
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def single_server_set_multi(hash, ttl, req_options)
|
|
562
|
+
pairs = hash.transform_keys { |k| @key_manager.validate_key(k.to_s) }
|
|
563
|
+
return unless (server = single_server)
|
|
564
|
+
|
|
565
|
+
server.request(:write_multi_req, pairs, ttl, req_options)
|
|
566
|
+
rescue Dalli::NetworkError
|
|
567
|
+
nil
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def single_server_delete_multi(keys)
|
|
571
|
+
validated_keys = keys.map { |k| @key_manager.validate_key(k.to_s) }
|
|
572
|
+
return unless (server = single_server)
|
|
573
|
+
|
|
574
|
+
server.request(:delete_multi_req, validated_keys)
|
|
575
|
+
rescue Dalli::NetworkError
|
|
576
|
+
nil
|
|
577
|
+
end
|
|
578
|
+
|
|
532
579
|
def get_multi_attributes(keys)
|
|
533
580
|
multi_trace_attrs('get_multi', keys.size, keys)
|
|
534
581
|
end
|
data/lib/dalli/protocol/meta.rb
CHANGED
|
@@ -257,6 +257,82 @@ module Dalli
|
|
|
257
257
|
@connection_manager.flush
|
|
258
258
|
end
|
|
259
259
|
|
|
260
|
+
# Single-server fast path for get_multi. Inlines request formatting and
|
|
261
|
+
# response parsing to minimize per-key overhead. Avoids the PipelinedGetter
|
|
262
|
+
# machinery (IO.select, response buffering, server grouping).
|
|
263
|
+
def read_multi_req(keys)
|
|
264
|
+
is_raw = raw_mode?
|
|
265
|
+
# Inline request formatting — avoids RequestFormatter.meta_get overhead per key.
|
|
266
|
+
# In raw mode: "mg <key> v k q s\r\n" (no f flag, key at index 2)
|
|
267
|
+
# Normal mode: "mg <key> v f k q s\r\n" (key at index 3)
|
|
268
|
+
post_get = is_raw ? " v k q s\r\n" : " v f k q s\r\n"
|
|
269
|
+
keys.each do |key|
|
|
270
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
|
271
|
+
write(base64 ? "mg #{encoded_key} b#{post_get}" : "mg #{encoded_key}#{post_get}")
|
|
272
|
+
end
|
|
273
|
+
write("mn\r\n")
|
|
274
|
+
@connection_manager.flush
|
|
275
|
+
|
|
276
|
+
read_multi_get_responses(is_raw)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def read_multi_get_responses(is_raw)
|
|
280
|
+
hash = {}
|
|
281
|
+
key_index = is_raw ? 2 : 3
|
|
282
|
+
while (line = @connection_manager.read_line)
|
|
283
|
+
break if line.start_with?('MN')
|
|
284
|
+
next unless line.start_with?('VA ')
|
|
285
|
+
|
|
286
|
+
key, value = parse_multi_get_value(line, key_index, is_raw)
|
|
287
|
+
hash[key] = value if key
|
|
288
|
+
end
|
|
289
|
+
hash
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def parse_multi_get_value(line, key_index, is_raw)
|
|
293
|
+
tokens = line.chomp!(TERMINATOR).split
|
|
294
|
+
value = @connection_manager.read(tokens[1].to_i + TERMINATOR.bytesize)&.chomp!(TERMINATOR)
|
|
295
|
+
raw_key = tokens[key_index]
|
|
296
|
+
return unless raw_key
|
|
297
|
+
|
|
298
|
+
key = KeyRegularizer.decode(raw_key[1..], tokens.include?('b'))
|
|
299
|
+
bitflags = is_raw ? 0 : response_processor.bitflags_from_tokens(tokens)
|
|
300
|
+
[key, @value_marshaller.retrieve(value, bitflags)]
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Single-server fast path for set_multi. Inlines request formatting to
|
|
304
|
+
# minimize per-key overhead. Avoids PipelinedSetter server grouping.
|
|
305
|
+
def write_multi_req(pairs, ttl, req_options)
|
|
306
|
+
ttl = TtlSanitizer.sanitize(ttl) if ttl
|
|
307
|
+
pairs.each do |key, raw_value|
|
|
308
|
+
(value, bitflags) = @value_marshaller.store(key, raw_value, req_options)
|
|
309
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
|
310
|
+
# Inline format: "ms <key> <size> c [b] F<flags> T<ttl> MS q\r\n"
|
|
311
|
+
cmd = "ms #{encoded_key} #{value.bytesize} c"
|
|
312
|
+
cmd << ' b' if base64
|
|
313
|
+
cmd << " F#{bitflags}" if bitflags
|
|
314
|
+
cmd << " T#{ttl}" if ttl
|
|
315
|
+
cmd << " MS q\r\n"
|
|
316
|
+
write(cmd)
|
|
317
|
+
write(value)
|
|
318
|
+
write(TERMINATOR)
|
|
319
|
+
end
|
|
320
|
+
write_noop
|
|
321
|
+
response_processor.consume_all_responses_until_mn
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Single-server fast path for delete_multi. Writes all quiet delete requests
|
|
325
|
+
# terminated by a noop, then consumes all responses.
|
|
326
|
+
def delete_multi_req(keys)
|
|
327
|
+
keys.each do |key|
|
|
328
|
+
encoded_key, base64 = KeyRegularizer.encode(key)
|
|
329
|
+
# Inline format: "md <key> [b] q\r\n"
|
|
330
|
+
write(base64 ? "md #{encoded_key} b q\r\n" : "md #{encoded_key} q\r\n")
|
|
331
|
+
end
|
|
332
|
+
write_noop
|
|
333
|
+
response_processor.consume_all_responses_until_mn
|
|
334
|
+
end
|
|
335
|
+
|
|
260
336
|
require_relative 'key_regularizer'
|
|
261
337
|
require_relative 'request_formatter'
|
|
262
338
|
require_relative 'response_processor'
|
data/lib/dalli/version.rb
CHANGED