Skip to content

Commit

Permalink
example adding support for arbitrary meta flags and for getting them …
Browse files Browse the repository at this point in the history
…back as part of the response
  • Loading branch information
danmayer committed Oct 16, 2024
1 parent 06be66d commit dfdd8f1
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ profile.html
## Environment normalisation:
/.bundle/
/lib/bundler/man/
dev.yml

# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
Expand Down
6 changes: 6 additions & 0 deletions lib/dalli/protocol/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ def ensure_connected!
connected?
end

def meta_flag_options(opts)
return nil unless opts.is_a?(Hash)

opts[:meta_flags]
end

def cache_nils?(opts)
return false unless opts.is_a?(Hash)

Expand Down
16 changes: 12 additions & 4 deletions lib/dalli/protocol/meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ def response_processor
# Retrieval Commands
def get(key, options = nil)
encoded_key, base64 = KeyRegularizer.encode(key)
req = RequestFormatter.meta_get(key: encoded_key, base64: base64)
req = RequestFormatter.meta_get(key: encoded_key, base64: base64, meta_flags: meta_flag_options(options))
write(req)
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
if meta_flag_options(options)
response_processor.meta_get_with_value_and_meta_flags(cache_nils: cache_nils?(options))
else
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
end
end

def quiet_get_request(key)
Expand All @@ -38,9 +42,13 @@ def quiet_get_request(key)
def gat(key, ttl, options = nil)
ttl = TtlSanitizer.sanitize(ttl)
encoded_key, base64 = KeyRegularizer.encode(key)
req = RequestFormatter.meta_get(key: encoded_key, ttl: ttl, base64: base64)
req = RequestFormatter.meta_get(key: encoded_key, ttl: ttl, base64: base64, meta_flags: meta_flag_options(options))
write(req)
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
if meta_flag_options(options)
response_processor.meta_get_with_value_and_meta_flags(cache_nils: cache_nils?(options))
else
response_processor.meta_get_with_value(cache_nils: cache_nils?(options))
end
end

def touch(key, ttl)
Expand Down
3 changes: 2 additions & 1 deletion lib/dalli/protocol/meta/request_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ class RequestFormatter
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/ParameterLists
# rubocop:disable Metrics/PerceivedComplexity
def self.meta_get(key:, value: true, return_cas: false, ttl: nil, base64: false, quiet: false)
def self.meta_get(key:, value: true, return_cas: false, ttl: nil, base64: false, quiet: false, meta_flags: nil)
cmd = "mg #{key}"
cmd << ' v f' if value
cmd << ' c' if return_cas
cmd << ' b' if base64
cmd << " T#{ttl}" if ttl
cmd << " #{meta_flags.join(' ')}" if meta_flags
cmd << ' k q s' if quiet # Return the key in the response if quiet
cmd + TERMINATOR
end
Expand Down
29 changes: 29 additions & 0 deletions lib/dalli/protocol/meta/response_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ def meta_get_with_value_and_cas
[@value_marshaller.retrieve(read_data(tokens[1].to_i), bitflags_from_tokens(tokens)), cas]
end

def meta_get_with_value_and_meta_flags(cache_nils: false)
tokens = error_on_unexpected!([VA, EN, HD])
return [(cache_nils ? ::Dalli::NOT_FOUND : nil), {}] if tokens.first == EN

meta_flags = {
c: cas_from_tokens(tokens),
h: hit_from_tokens(tokens),
l: last_accessed_from_tokens(tokens),
t: ttl_remaining_from_tokens(tokens),
}
return [(cache_nils ? ::Dalli::NOT_FOUND : nil), meta_flags] unless tokens.first == VA

value, bitflag = @value_marshaller.retrieve(read_data(tokens[1].to_i), bitflags_from_tokens(tokens))
meta_flags[:bitflag] = bitflag
[value, meta_flags]
end

def meta_get_without_value
tokens = error_on_unexpected!([EN, HD])
tokens.first == EN ? nil : true
Expand Down Expand Up @@ -180,6 +197,18 @@ def cas_from_tokens(tokens)
value_from_tokens(tokens, 'c')&.to_i
end

def hit_from_tokens(tokens)
value_from_tokens(tokens, 'h')&.to_i != 0
end

def last_accessed_from_tokens(tokens)
value_from_tokens(tokens, 'l')&.to_i
end

def ttl_remaining_from_tokens(tokens)
value_from_tokens(tokens, 't')&.to_i
end

def key_from_tokens(tokens)
encoded_key = value_from_tokens(tokens, 'k')
base64_encoded = tokens.any?('b')
Expand Down
21 changes: 21 additions & 0 deletions test/integration/test_operations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@
end
end

it 'return the value and the meta flags' do
memcached_persistent(p) do |dc|
skip 'not supported' if p == :binary
dc.flush

val1 = "meta"
dc.set('meta_key', val1)
response = dc.get('meta_key', meta_flags: [:l, :h, :t])
# ensure hit true and last accessed 0
response = dc.get('meta_key', meta_flags: [:l, :h, :t])
val2 = response.first
meta_flags = response.last

assert_equal val1, val2
assert_equal meta_flags, {c: 0, l: 0, h: true, t: -1, bitflag: nil}

assert op_addset_succeeds(dc.set('a', nil))
assert_nil dc.get('a')
end
end

it 'returns nil on a miss' do
memcached_persistent(p) do |dc|
assert_nil dc.get('notexist')
Expand Down

0 comments on commit dfdd8f1

Please sign in to comment.