dalli 5.0.2 → 5.0.4
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 +35 -0
- data/README.md +14 -0
- data/lib/dalli/client.rb +3 -3
- data/lib/dalli/flags.rb +15 -0
- data/lib/dalli/instrumentation.rb +12 -2
- data/lib/dalli/protocol/value_compressor.rb +2 -6
- data/lib/dalli/protocol/value_serializer.rb +10 -12
- data/lib/dalli/socket.rb +9 -4
- data/lib/dalli/version.rb +1 -1
- data/lib/dalli.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b211a810c4d54d2d4d92203b390e8b29260f725e6dae699ca04703ac42cef8e9
|
|
4
|
+
data.tar.gz: 4d765c4491412ad4b9c8ab6038232436cae5824db2abaffc20119f2dfb7fa1ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a09168f456e5ce4691d0e24b128997c3f87bee7e046592a600490bc8e9526d1ce681d156fc9997c7045e623017ee0df1273d46c16656820b0f4933b8acc42cbb
|
|
7
|
+
data.tar.gz: 14e72c85d60582e03b19f04b4fd822a2e789fc6f780ae3f55944271dc25f0eeb7d6de0acade39bedf4729710731d273f8501f2078dc690fa3093926041b8d178
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
Dalli Changelog
|
|
2
2
|
=====================
|
|
3
3
|
|
|
4
|
+
Unreleased
|
|
5
|
+
==========
|
|
6
|
+
|
|
7
|
+
5.0.4
|
|
8
|
+
==========
|
|
9
|
+
|
|
10
|
+
Bug fixes:
|
|
11
|
+
|
|
12
|
+
- Fix `string_fastpath` flag collision with compression (#1099)
|
|
13
|
+
- `ValueSerializer::FLAG_UTF8` and `ValueCompressor::FLAG_COMPRESSED` were both `0x2`, causing `Dalli::UnmarshalError` on any UTF-8 string written with `string_fastpath: true` when compression is enabled, and silent encoding corruption for binary strings
|
|
14
|
+
- Introduces `Dalli::Flags` to centralise bit flag constants; UTF8 is reassigned to `0x4`
|
|
15
|
+
- Adds regression test covering short/long UTF-8, binary, and cross-client read scenarios
|
|
16
|
+
- Thanks to Jean Boussier and Mikael Henriksson for the fix and regression test
|
|
17
|
+
|
|
18
|
+
- Fix client-level `string_fastpath: true` being silently ignored (#1101)
|
|
19
|
+
- `Dalli::Client.new(servers, string_fastpath: true)` had no effect; the fast path was only taken when `string_fastpath: true` was passed as a per-request option on each `set` call
|
|
20
|
+
- Per-request option continues to take precedence over the client-level setting in both directions
|
|
21
|
+
|
|
22
|
+
5.0.3
|
|
23
|
+
==========
|
|
24
|
+
|
|
25
|
+
Performance:
|
|
26
|
+
|
|
27
|
+
- Eliminate double array allocation in `Client#perform` (#1093)
|
|
28
|
+
- Changed method signature from `perform(*all_args)` with destructuring to `perform(op, key, *args)`, letting Ruby decompose arguments directly without intermediate array allocations
|
|
29
|
+
- Reduces benchmark time by ~39% across all Dalli operations (get, set, delete, etc.)
|
|
30
|
+
- Thanks to Sam Obeid for this contribution
|
|
31
|
+
|
|
32
|
+
Features:
|
|
33
|
+
|
|
34
|
+
- Support `connect_timeout:` keyword argument with `resolv-replace` >= 0.2.0, which now correctly forwards keyword arguments through its `TCPSocket` patch (#1096)
|
|
35
|
+
|
|
36
|
+
- Add `Dalli::Instrumentation.disable!` to allow disabling OpenTelemetry instrumentation at runtime (#1088)
|
|
37
|
+
- Also exposes `Dalli::Instrumentation.tracer=` for setting a custom tracer
|
|
38
|
+
|
|
4
39
|
5.0.2
|
|
5
40
|
==========
|
|
6
41
|
|
data/README.md
CHANGED
|
@@ -90,6 +90,20 @@ Exceptions are automatically recorded on spans with error status. When an operat
|
|
|
90
90
|
2. The span status is set to error with the exception message
|
|
91
91
|
3. The exception is re-raised to the caller
|
|
92
92
|
|
|
93
|
+
### Disabling Instrumentation
|
|
94
|
+
|
|
95
|
+
To disable instrumentation at runtime (e.g., in tests or specific environments):
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
Dalli::Instrumentation.disable!
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You can also assign a custom tracer directly:
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
Dalli::Instrumentation.tracer = my_custom_tracer
|
|
105
|
+
```
|
|
106
|
+
|
|
93
107
|
### Zero Overhead
|
|
94
108
|
|
|
95
109
|
When OpenTelemetry is not present, there is zero overhead - the tracing code checks once at startup and bypasses all instrumentation logic entirely when the SDK is not loaded.
|
data/lib/dalli/client.rb
CHANGED
|
@@ -650,11 +650,11 @@ module Dalli
|
|
|
650
650
|
# a particular memcached instance becomes unreachable, or the
|
|
651
651
|
# operation times out.
|
|
652
652
|
##
|
|
653
|
-
|
|
653
|
+
# rubocop:disable Naming/MethodParameterName
|
|
654
|
+
def perform(op, key, *args)
|
|
655
|
+
# rubocop:enable Naming/MethodParameterName
|
|
654
656
|
return yield if block_given?
|
|
655
657
|
|
|
656
|
-
op, key, *args = all_args
|
|
657
|
-
|
|
658
658
|
key = key.to_s
|
|
659
659
|
key = @key_manager.validate_key(key)
|
|
660
660
|
|
data/lib/dalli/flags.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dalli
|
|
4
|
+
module Flags
|
|
5
|
+
# https://www.hjp.at/zettel/m/memcached_flags.rxml
|
|
6
|
+
# Looks like most clients use bit 0 to indicate native language serialization
|
|
7
|
+
SERIALIZED = 0x1
|
|
8
|
+
|
|
9
|
+
# https://www.hjp.at/zettel/m/memcached_flags.rxml
|
|
10
|
+
# Looks like most clients use bit 1 to indicate gzip compression.
|
|
11
|
+
COMPRESSED = 0x2
|
|
12
|
+
|
|
13
|
+
UTF8 = 0x4
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -61,13 +61,14 @@ module Dalli
|
|
|
61
61
|
# Uses the library name 'dalli' and current Dalli::VERSION.
|
|
62
62
|
#
|
|
63
63
|
# @return [OpenTelemetry::Trace::Tracer, nil] the tracer or nil if OTel unavailable
|
|
64
|
-
# rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
64
|
+
# rubocop:disable ThreadSafety/ClassInstanceVariable, ThreadSafety/ClassAndModuleAttributes
|
|
65
65
|
def tracer
|
|
66
66
|
return @tracer if defined?(@tracer)
|
|
67
67
|
|
|
68
68
|
@tracer = (OpenTelemetry.tracer_provider.tracer('dalli', Dalli::VERSION) if defined?(OpenTelemetry))
|
|
69
69
|
end
|
|
70
|
-
|
|
70
|
+
|
|
71
|
+
attr_writer :tracer
|
|
71
72
|
|
|
72
73
|
# Returns true if instrumentation is enabled (OpenTelemetry SDK is available).
|
|
73
74
|
#
|
|
@@ -76,6 +77,15 @@ module Dalli
|
|
|
76
77
|
!tracer.nil?
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
# Disable instrumentation.
|
|
81
|
+
#
|
|
82
|
+
# @return [nil]
|
|
83
|
+
def disable!
|
|
84
|
+
@tracer = nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# rubocop:enable ThreadSafety/ClassInstanceVariable, ThreadSafety/ClassAndModuleAttributes
|
|
88
|
+
|
|
79
89
|
# Wraps a block with a span if instrumentation is enabled.
|
|
80
90
|
#
|
|
81
91
|
# Creates a client span with the given name and attributes merged with
|
|
@@ -20,10 +20,6 @@ module Dalli
|
|
|
20
20
|
|
|
21
21
|
OPTIONS = DEFAULTS.keys.freeze
|
|
22
22
|
|
|
23
|
-
# https://www.hjp.at/zettel/m/memcached_flags.rxml
|
|
24
|
-
# Looks like most clients use bit 1 to indicate gzip compression.
|
|
25
|
-
FLAG_COMPRESSED = 0x2
|
|
26
|
-
|
|
27
23
|
def initialize(client_options)
|
|
28
24
|
@compression_options =
|
|
29
25
|
DEFAULTS.merge(client_options.slice(*OPTIONS))
|
|
@@ -32,13 +28,13 @@ module Dalli
|
|
|
32
28
|
def store(value, req_options, bitflags)
|
|
33
29
|
do_compress = compress_value?(value, req_options)
|
|
34
30
|
store_value = do_compress ? compressor.compress(value) : value
|
|
35
|
-
bitflags |=
|
|
31
|
+
bitflags |= Flags::COMPRESSED if do_compress
|
|
36
32
|
|
|
37
33
|
[store_value, bitflags]
|
|
38
34
|
end
|
|
39
35
|
|
|
40
36
|
def retrieve(value, bitflags)
|
|
41
|
-
compressed = bitflags.anybits?(
|
|
37
|
+
compressed = bitflags.anybits?(Flags::COMPRESSED)
|
|
42
38
|
compressed ? compressor.decompress(value) : value
|
|
43
39
|
|
|
44
40
|
# TODO: We likely want to move this rescue into the Dalli::Compressor / Dalli::GzipCompressor
|
|
@@ -10,16 +10,12 @@ module Dalli
|
|
|
10
10
|
##
|
|
11
11
|
class ValueSerializer
|
|
12
12
|
DEFAULTS = {
|
|
13
|
-
serializer: Marshal
|
|
13
|
+
serializer: Marshal,
|
|
14
|
+
string_fastpath: false
|
|
14
15
|
}.freeze
|
|
15
16
|
|
|
16
17
|
OPTIONS = DEFAULTS.keys.freeze
|
|
17
18
|
|
|
18
|
-
# https://www.hjp.at/zettel/m/memcached_flags.rxml
|
|
19
|
-
# Looks like most clients use bit 0 to indicate native language serialization
|
|
20
|
-
FLAG_SERIALIZED = 0x1
|
|
21
|
-
FLAG_UTF8 = 0x2
|
|
22
|
-
|
|
23
19
|
# Class variable to track whether the Marshal warning has been logged
|
|
24
20
|
@@marshal_warning_logged = false # rubocop:disable Style/ClassVars
|
|
25
21
|
|
|
@@ -35,18 +31,18 @@ module Dalli
|
|
|
35
31
|
return store_raw(value, bitflags) if req_options&.dig(:raw)
|
|
36
32
|
return store_string_fastpath(value, bitflags) if use_string_fastpath?(value, req_options)
|
|
37
33
|
|
|
38
|
-
[serialize_value(value), bitflags |
|
|
34
|
+
[serialize_value(value), bitflags | Flags::SERIALIZED]
|
|
39
35
|
end
|
|
40
36
|
|
|
41
37
|
def retrieve(value, bitflags)
|
|
42
|
-
serialized = bitflags.anybits?(
|
|
38
|
+
serialized = bitflags.anybits?(Flags::SERIALIZED)
|
|
43
39
|
if serialized
|
|
44
40
|
begin
|
|
45
41
|
serializer.load(value)
|
|
46
42
|
rescue StandardError
|
|
47
43
|
raise UnmarshalError, 'Unable to unmarshal value'
|
|
48
44
|
end
|
|
49
|
-
elsif bitflags.anybits?(
|
|
45
|
+
elsif bitflags.anybits?(Flags::UTF8)
|
|
50
46
|
value.force_encoding(Encoding::UTF_8)
|
|
51
47
|
else
|
|
52
48
|
value
|
|
@@ -86,13 +82,15 @@ module Dalli
|
|
|
86
82
|
def store_string_fastpath(value, bitflags)
|
|
87
83
|
case value.encoding
|
|
88
84
|
when Encoding::BINARY then [value, bitflags]
|
|
89
|
-
when Encoding::UTF_8 then [value, bitflags |
|
|
90
|
-
else [serialize_value(value), bitflags |
|
|
85
|
+
when Encoding::UTF_8 then [value, bitflags | Flags::UTF8]
|
|
86
|
+
else [serialize_value(value), bitflags | Flags::SERIALIZED]
|
|
91
87
|
end
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
def use_string_fastpath?(value, req_options)
|
|
95
|
-
req_options&.dig(:string_fastpath)
|
|
91
|
+
fastpath = req_options&.dig(:string_fastpath)
|
|
92
|
+
fastpath = @serialization_options[:string_fastpath] if fastpath.nil?
|
|
93
|
+
fastpath && value.instance_of?(String)
|
|
96
94
|
end
|
|
97
95
|
|
|
98
96
|
def warn_if_marshal_default(protocol_options)
|
data/lib/dalli/socket.rb
CHANGED
|
@@ -106,14 +106,19 @@ module Dalli
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
# Detect and cache whether TCPSocket supports the connect_timeout: keyword argument.
|
|
109
|
-
# Returns
|
|
110
|
-
#
|
|
109
|
+
# Returns true for an unmodified TCPSocket on Ruby 3.0+, or for resolv-replace >= 0.2.0
|
|
110
|
+
# which forwards keyword arguments through its patch.
|
|
111
|
+
# Returns false when monkey-patched by gems like socksify or resolv-replace < 0.2.0.
|
|
111
112
|
# rubocop:disable ThreadSafety/ClassInstanceVariable
|
|
112
113
|
def self.supports_connect_timeout?
|
|
113
114
|
return @supports_connect_timeout if defined?(@supports_connect_timeout)
|
|
114
115
|
|
|
115
|
-
@supports_connect_timeout = RUBY_VERSION >= '3.0' &&
|
|
116
|
-
::TCPSocket.instance_method(:initialize).parameters
|
|
116
|
+
@supports_connect_timeout = RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '3.0' &&
|
|
117
|
+
::TCPSocket.instance_method(:initialize).parameters.then do |params|
|
|
118
|
+
params == TCPSOCKET_NATIVE_PARAMETERS || params.any? do |type, _|
|
|
119
|
+
type == :keyrest
|
|
120
|
+
end
|
|
121
|
+
end
|
|
117
122
|
end
|
|
118
123
|
# rubocop:enable ThreadSafety/ClassInstanceVariable
|
|
119
124
|
|
data/lib/dalli/version.rb
CHANGED
data/lib/dalli.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dalli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.0.
|
|
4
|
+
version: 5.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter M. Goldstein
|
|
@@ -40,6 +40,7 @@ files:
|
|
|
40
40
|
- lib/dalli/cas/client.rb
|
|
41
41
|
- lib/dalli/client.rb
|
|
42
42
|
- lib/dalli/compressor.rb
|
|
43
|
+
- lib/dalli/flags.rb
|
|
43
44
|
- lib/dalli/instrumentation.rb
|
|
44
45
|
- lib/dalli/key_manager.rb
|
|
45
46
|
- lib/dalli/options.rb
|
|
@@ -87,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
87
88
|
- !ruby/object:Gem::Version
|
|
88
89
|
version: '0'
|
|
89
90
|
requirements: []
|
|
90
|
-
rubygems_version: 4.0.
|
|
91
|
+
rubygems_version: 4.0.10
|
|
91
92
|
specification_version: 4
|
|
92
93
|
summary: High performance memcached client for Ruby
|
|
93
94
|
test_files: []
|