-
Notifications
You must be signed in to change notification settings - Fork 5
follow up on ways to reduce allocation on multi_get #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a6b0c90
489e2fd
ed0f581
f46f6bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,10 +150,10 @@ def quiet? | |
| alias multi? quiet? | ||
|
|
||
| # NOTE: Additional public methods should be overridden in Dalli::Threadsafe | ||
| ALLOWED_QUIET_OPS = %i[add replace set delete incr decr append prepend flush noop].freeze | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not understanding the reason for making this public (at least as part of this PR)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rubocop update complained about private constant. |
||
|
|
||
| private | ||
|
|
||
| ALLOWED_QUIET_OPS = %i[add replace set delete incr decr append prepend flush noop].freeze | ||
| def verify_allowed_quiet!(opkey) | ||
| return if ALLOWED_QUIET_OPS.include?(opkey) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,8 +11,15 @@ module Protocol | |
| # protocol. Contains logic for managing connection state to the server (retries, etc), | ||
| # formatting requests to the server, and unpacking responses. | ||
| ## | ||
| class Meta < Base | ||
| class Meta < Base # rubocop:disable Metrics/ClassLength | ||
| TERMINATOR = "\r\n" | ||
| META_NOOP = "mn\r\n" | ||
| META_NOOP_RESP = 'MN' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where's this used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah these got refactored away, I can remove them |
||
| META_VALUE_RESP = 'VA' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above |
||
| META_GET_REQ_NO_FLAGS = "v k q\r\n" | ||
| META_GET_REQ = "v f k q\r\n" | ||
| M_CONSTANT = 'M'.ord | ||
| V_CONSTANT = 'V'.ord | ||
| SUPPORTS_CAPACITY = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') | ||
|
|
||
| def response_processor | ||
|
|
@@ -57,27 +64,46 @@ def read_multi_req(keys) | |
| # Pre-allocate the results hash with expected size | ||
| results = SUPPORTS_CAPACITY ? Hash.new(nil, capacity: keys.size) : {} | ||
| optimized_for_raw = @value_marshaller.raw_by_default | ||
| key_index = optimized_for_raw ? 2 : 3 | ||
| terminator_buffer = String.new(TERMINATOR, capacity: TERMINATOR.size) | ||
|
|
||
| post_get_req = optimized_for_raw ? "v k q\r\n" : "v f k q\r\n" | ||
| post_get_req = optimized_for_raw ? META_GET_REQ_NO_FLAGS : META_GET_REQ | ||
| keys.each do |key| | ||
| @connection_manager.write("mg #{key} #{post_get_req}") | ||
| end | ||
| @connection_manager.write("mn\r\n") | ||
| @connection_manager.write(META_NOOP) | ||
| @connection_manager.flush | ||
|
|
||
| terminator_length = TERMINATOR.length | ||
| while (line = @connection_manager.readline) | ||
| break if line == TERMINATOR || line[0, 2] == 'MN' | ||
| next unless line[0, 3] == 'VA ' | ||
|
|
||
| # VA value_length flags key | ||
| tokens = line.split | ||
| value = @connection_manager.read_exact(tokens[1].to_i) | ||
| bitflags = optimized_for_raw ? 0 : @response_processor.bitflags_from_tokens(tokens) | ||
| @connection_manager.read_exact(terminator_length) # read the terminator | ||
| results[tokens[key_index].byteslice(1..-1)] = | ||
| @value_marshaller.retrieve(value, bitflags) | ||
| while (byte = @connection_manager.read_byte) | ||
| if byte == M_CONSTANT | ||
| @connection_manager.readline | ||
| break | ||
| end | ||
| unless byte == V_CONSTANT | ||
| @connection_manager.readline | ||
| next | ||
| end | ||
|
|
||
| @connection_manager.read_byte # skip 'A' | ||
| @connection_manager.read_byte # skip terminator | ||
| line = @connection_manager.readline | ||
| # VA value_length kNAME\r\n | ||
| # if rindex and linex are equal split out flags | ||
| right_seperator_index = line.rindex(' ') | ||
| left_seperator_index = line.index(' ') | ||
| bitflags = if right_seperator_index == left_seperator_index | ||
| 0 | ||
| else | ||
| line.byteslice(left_seperator_index + 2, right_seperator_index - left_seperator_index - 1).to_i | ||
| end | ||
|
|
||
| # +2 on index skips the space and 'k', then - 4 for the ' k' and "\r\n" | ||
| key = line.byteslice(right_seperator_index + 2, (line.length - right_seperator_index - 4)) | ||
|
|
||
| value = @connection_manager.read(line.to_i) | ||
| @connection_manager.read_to_outstring(terminator_length, terminator_buffer) | ||
| results[key] = | ||
| bitflags.zero? ? value : @value_marshaller.retrieve(value, bitflags) | ||
| end | ||
| results | ||
| end | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ class ResponseProcessor | |
| def initialize(io_source, value_marshaller) | ||
| @io_source = io_source | ||
| @value_marshaller = value_marshaller | ||
| @terminator_buffer = String.new(TERMINATOR, capacity: TERMINATOR.size) | ||
| end | ||
|
|
||
| def meta_get_with_value(cache_nils: false, skip_flags: false) | ||
|
|
@@ -249,7 +250,7 @@ def next_line_to_tokens | |
|
|
||
| def read_data(data_size) | ||
| resp_data = @io_source.read(data_size) | ||
| @io_source.read(TERMINATOR.bytesize) | ||
| @io_source.read_to_outstring(TERMINATOR.bytesize, @terminator_buffer) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this removes an additional allocation for all the non multi calls |
||
| resp_data | ||
| end | ||
| end | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.