Skip to content

Commit 2351639

Browse files
authored
optimization around get for raw usage (#44)
* optimization around get for raw usage * add tests ensuring the raw optimized code flow is used * fix flaky test
1 parent 3700ea5 commit 2351639

File tree

5 files changed

+76
-10
lines changed

5 files changed

+76
-10
lines changed

lib/dalli/protocol/meta.rb

+29-6
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,18 @@ def write_multi_storage_req(_mode, pairs, ttl = nil, _cas = nil, options = {})
5050
end
5151

5252
# rubocop:disable Metrics/AbcSize
53+
# rubocop:disable Metrics/MethodLength
54+
# rubocop:disable Metrics/CyclomaticComplexity
55+
# rubocop:disable Metrics/PerceivedComplexity
5356
def read_multi_req(keys)
5457
# Pre-allocate the results hash with expected size
5558
results = SUPPORTS_CAPACITY ? Hash.new(nil, capacity: keys.size) : {}
59+
optimized_for_raw = @value_marshaller.raw_by_default
60+
key_index = optimized_for_raw ? 2 : 3
5661

62+
post_get_req = optimized_for_raw ? "v k q\r\n" : "v f k q\r\n"
5763
keys.each do |key|
58-
@connection_manager.write("mg #{key} v f k q\r\n")
64+
@connection_manager.write("mg #{key} #{post_get_req}")
5965
end
6066
@connection_manager.write("mn\r\n")
6167
@connection_manager.flush
@@ -68,25 +74,42 @@ def read_multi_req(keys)
6874
# VA value_length flags key
6975
tokens = line.split
7076
value = @connection_manager.read_exact(tokens[1].to_i)
77+
bitflags = optimized_for_raw ? 0 : @response_processor.bitflags_from_tokens(tokens)
7178
@connection_manager.read_exact(terminator_length) # read the terminator
72-
results[tokens[3].byteslice(1..-1)] =
73-
@value_marshaller.retrieve(value, @response_processor.bitflags_from_tokens(tokens))
79+
results[tokens[key_index].byteslice(1..-1)] =
80+
@value_marshaller.retrieve(value, bitflags)
7481
end
7582
results
7683
end
7784
# rubocop:enable Metrics/AbcSize
85+
# rubocop:enable Metrics/MethodLength
86+
# rubocop:enable Metrics/CyclomaticComplexity
87+
# rubocop:enable Metrics/PerceivedComplexity
7888

7989
# Retrieval Commands
90+
# rubocop:disable Metrics/AbcSize
91+
# rubocop:disable Metrics/CyclomaticComplexity
92+
# rubocop:disable Metrics/PerceivedComplexity
8093
def get(key, options = nil)
8194
encoded_key, base64 = KeyRegularizer.encode(key)
82-
req = RequestFormatter.meta_get(key: encoded_key, base64: base64, meta_flags: meta_flag_options(options))
83-
write(req)
84-
if meta_flag_options(options)
95+
meta_options = meta_flag_options(options)
96+
97+
if !meta_options && !base64 && !quiet? && @value_marshaller.raw_by_default
98+
write("mg #{encoded_key} v\r\n")
99+
else
100+
write(RequestFormatter.meta_get(key: encoded_key, base64: base64, meta_flags: meta_options))
101+
end
102+
if !meta_options && !base64 && !quiet? && @value_marshaller.raw_by_default
103+
response_processor.meta_get_with_value(cache_nils: cache_nils?(options), skip_flags: true)
104+
elsif meta_options
85105
response_processor.meta_get_with_value_and_meta_flags(cache_nils: cache_nils?(options))
86106
else
87107
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
88108
end
89109
end
110+
# rubocop:enable Metrics/AbcSize
111+
# rubocop:enable Metrics/CyclomaticComplexity
112+
# rubocop:enable Metrics/PerceivedComplexity
90113

91114
def quiet_get_request(key)
92115
encoded_key, base64 = KeyRegularizer.encode(key)

lib/dalli/protocol/meta/response_processor.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ def initialize(io_source, value_marshaller)
2828
@value_marshaller = value_marshaller
2929
end
3030

31-
def meta_get_with_value(cache_nils: false)
31+
def meta_get_with_value(cache_nils: false, skip_flags: false)
3232
tokens = error_on_unexpected!([VA, EN, HD])
3333
return cache_nils ? ::Dalli::NOT_FOUND : nil if tokens.first == EN
3434
return true unless tokens.first == VA
3535

36-
@value_marshaller.retrieve(read_data(tokens[1].to_i), bitflags_from_tokens(tokens))
36+
if skip_flags
37+
@value_marshaller.retrieve(read_data(tokens[1].to_i), 0)
38+
else
39+
@value_marshaller.retrieve(read_data(tokens[1].to_i), bitflags_from_tokens(tokens))
40+
end
3741
end
3842

3943
def meta_get_with_value_and_cas

lib/dalli/protocol/value_marshaller.rb

+3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ class ValueMarshaller
2222
def_delegators :@value_serializer, :serializer
2323
def_delegators :@value_compressor, :compressor, :compression_min_size, :compress_by_default?
2424

25+
attr_reader :raw_by_default
26+
2527
def initialize(client_options)
2628
@value_serializer = ValueSerializer.new(client_options)
2729
@value_compressor = ValueCompressor.new(client_options)
30+
@raw_by_default = client_options[:raw] || false
2831

2932
@marshal_options =
3033
DEFAULTS.merge(client_options.slice(*OPTIONS))

test/integration/test_operations.rb

+16-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@
2121
end
2222
end
2323

24+
it 'returns the value on a hit when using raw single server optimized get' do
25+
memcached_persistent do |_, port|
26+
dc = Dalli::Client.new("localhost:#{port}", namespace: 'some:namspace', raw: true)
27+
dc.close
28+
dc.flush
29+
30+
val1 = '1234567890' * 50
31+
dc.set('a', val1)
32+
val2 = dc.get('a')
33+
34+
assert_equal val1, val2
35+
end
36+
end
37+
2438
it 'return the value that include TERMINATOR on a hit' do
2539
memcached_persistent do |dc|
2640
dc.flush
@@ -46,14 +60,14 @@
4660

4761
assert_equal val1, val2
4862
# not yet hit, and last accessed 0 from set
49-
assert_equal({ c: 0, l: 0, h: false, t: -1, bitflag: nil }, meta_flags)
63+
assert_equal({ c: 0, l: 0, h: false, t: -1, bitflag: nil }.sort, meta_flags.sort)
5064

5165
sleep 1 # we can't simulate time in memcached so we need to sleep
5266
# ensure hit true and last accessed 1
5367
val2, meta_flags = dc.get('meta_key', meta_flags: %i[l h t])
5468

5569
assert_equal val1, val2
56-
assert_equal({ c: 0, l: 1, h: true, t: -1, bitflag: nil }, meta_flags)
70+
assert_equal({ c: 0, l: 1, h: true, t: -1, bitflag: nil }.sort, meta_flags.sort)
5771

5872
assert op_addset_succeeds(dc.set('meta_key', nil))
5973
assert_nil dc.get('meta_key')

test/integration/test_pipelined_get.rb

+22
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,28 @@
7575
end
7676
end
7777

78+
it 'meta read_multi_req supports optimized raw get' do
79+
memcached_persistent do |_, port|
80+
dc = Dalli::Client.new("localhost:#{port}", namespace: 'some:namspace', raw: true)
81+
dc.close
82+
dc.flush
83+
84+
keys_to_query = %w[a b]
85+
86+
resp = dc.get_multi(keys_to_query)
87+
88+
assert_empty(resp)
89+
90+
dc.set('a', 'foo')
91+
dc.set('b', 'bar')
92+
93+
resp = dc.get_multi(keys_to_query)
94+
expected_resp = { 'a' => 'foo', 'b' => 'bar' }
95+
96+
assert_equal(expected_resp, resp)
97+
end
98+
end
99+
78100
it 'meta read_multi_req supports pipelined get doesnt default value on misses' do
79101
memcached_persistent do |_, port|
80102
dc = Dalli::Client.new("localhost:#{port}", namespace: 'some:namspace')

0 commit comments

Comments
 (0)