dalli 5.2.4 → 5.2.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 626c8dbb98798247423221395d6d518a87ad9ce64ce268488ecd4eb7d8d7f347
4
- data.tar.gz: 999126be42f4c80be3eb566d3f10dfbb8e10b46446aaaf0da546523c0e734eef
3
+ metadata.gz: db76345a60e108cc8a1aa4f39adae0e657b200b4e3f17f7196702776c1ff8bc4
4
+ data.tar.gz: eb643908bb2aa3654f2d7c82bcce21fa82159931e162cdf17bd79d9fe40b2ed0
5
5
  SHA512:
6
- metadata.gz: 34aaa230697fd4ac27118ec8a90608d30bdc8dabaf9fc5cfc1b8c4e56a20c701708f1b6b7052fd76c7363f25e238905648296865c41b770f434457005af7aac8
7
- data.tar.gz: 7bf1bbe810632bd9733a9cb122900ff9e1b283ade2ef071cec13d609cf4e0b2580de06f1116e9a71685fe98aa7804022c719ef3b921e41e9eb76f2be98b5ab1a
6
+ metadata.gz: e5d227f5b4e7fda6e35907f0d94b74fcd528d0c203ce6a7c5b01ab33e2908fe78147c70df8661eedbc2b82f1024b4c89bb890fc0418745150c379805f87afea2
7
+ data.tar.gz: dbb2e3d7bb7b1c852a8c0d8b359f1621d0aa0f3ef32dbe1ca449e384325a929e0db6ba893357195e284c84162ce3ae4a1ce39e9f86b5b1bfde149a3725ae3a45
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ Dalli Changelog
4
4
  Unreleased
5
5
  ==========
6
6
 
7
+ 5.2.6
8
+ ==========
9
+
10
+ Bug fixes:
11
+
12
+ - Raw values in an encoding that isn't ASCII-compatible, such as UTF-16, raised `Encoding::CompatibilityError` from `set`, `add`, `replace`, `append`, `prepend` and `set_multi`, as did a single-server `set_multi` mixing non-ASCII UTF-8 values with binary ones (such as raw Strings of both kinds, or with a JSON serializer, a value containing `é` next to a compressed one). A `p_token` or `l_token` with non-ASCII characters, such as `é`, did the same for any binary value, including every Marshal-serialized one. The error also closed the connection, so in a `quiet` block, or within one server's part of a multi-server `set_multi`, writes buffered before it were silently lost. Values are now sent as their bytes, which is what memcached stores (#1269)
13
+
14
+ 5.2.5
15
+ ==========
16
+
17
+ Bug fixes:
18
+
19
+ - `set_multi` across more than one server stored `nil` for a Symbol key or a key over 250 bytes, in place of the value given
20
+
7
21
  5.2.4
8
22
  ==========
9
23
 
@@ -43,9 +43,12 @@ module Dalli
43
43
 
44
44
  private
45
45
 
46
+ # Values are looked up by each key as sent. The caller's key can't be
47
+ # rebuilt from it: a long key is digested, and a Symbol becomes a String.
46
48
  def setup_requests(hash, ttl, req_options)
47
- groups = groups_for_keys(hash.keys)
48
- make_set_requests(groups, hash, ttl, req_options)
49
+ pairs = hash.transform_keys { |k| @key_manager.validate_key(k.to_s) }
50
+ groups = groups_for_keys(pairs.keys)
51
+ make_set_requests(groups, pairs, ttl, req_options)
49
52
  groups.keys
50
53
  end
51
54
 
@@ -59,12 +62,10 @@ module Dalli
59
62
  # RetryableNetworkError too -- since NetworkError < DalliError -- dropping
60
63
  # this server's keys on a transient hiccup instead of retrying. Only a
61
64
  # non-network DalliError should be swallowed here.
62
- def make_set_requests(groups, hash, ttl, req_options)
65
+ def make_set_requests(groups, pairs, ttl, req_options)
63
66
  groups.each do |server, keys_for_server|
64
67
  keys_for_server.each do |key|
65
- original_key = @key_manager.key_without_namespace(key)
66
- value = hash[original_key]
67
- server.request(:pipelined_set, key, value, ttl, req_options)
68
+ server.request(:pipelined_set, key, pairs[key], ttl, req_options)
68
69
  rescue Dalli::NetworkError
69
70
  raise
70
71
  rescue Dalli::MarshalError, Dalli::ValueOverMaxSize => e
@@ -93,8 +94,7 @@ module Dalli
93
94
  end
94
95
 
95
96
  def groups_for_keys(keys)
96
- validated_keys = keys.map { |k| @key_manager.validate_key(k.to_s) }
97
- groups = @ring.keys_grouped_by_server(validated_keys)
97
+ groups = @ring.keys_grouped_by_server(keys)
98
98
 
99
99
  if (unfound_keys = groups.delete(nil))
100
100
  Dalli.logger.debug do
@@ -167,7 +167,7 @@ module Dalli
167
167
  ttl: ttl, mode: mode, quiet: quiet,
168
168
  **routing_token_kwargs(options))
169
169
  end
170
- write("#{req}#{value}#{TERMINATOR}")
170
+ write(request_with_value(req, value))
171
171
  finish_write(quiet)
172
172
  end
173
173
  # rubocop:enable Metrics/ParameterLists
@@ -188,10 +188,20 @@ module Dalli
188
188
  req = RequestFormatter.meta_set(key: key, value: value,
189
189
  cas: cas, ttl: ttl, mode: mode, quiet: quiet?,
190
190
  **routing_token_kwargs(options))
191
- write("#{req}#{value}#{TERMINATOR}")
191
+ write(request_with_value(req, value))
192
192
  finish_write(quiet?)
193
193
  end
194
194
 
195
+ # Ruby refuses to join Strings whose encodings clash, such as a raw
196
+ # UTF-16 value, or a routing token with non-ASCII characters next to a
197
+ # binary value. memcached stores bytes, so join the bytes then.
198
+ # Joining the Strings first keeps the common case free of extra checks.
199
+ def request_with_value(req, value)
200
+ "#{req}#{value}#{TERMINATOR}"
201
+ rescue Encoding::CompatibilityError
202
+ "#{req.b}#{value.b}#{TERMINATOR}"
203
+ end
204
+
195
205
  # Delete Commands
196
206
  #
197
207
  # `options` supports the meta-delete keys :invalidate, :tombstone_ttl and
@@ -120,8 +120,9 @@ module Dalli
120
120
 
121
121
  def multi_meta_set(entries, ttl: nil, p_token: nil, l_token: nil)
122
122
  # Routing tokens apply to every entry in the batch, so the suffix is
123
- # built once rather than per entry.
124
- token_suffix = routing_tokens(p_token: p_token, l_token: l_token)
123
+ # built once rather than per entry. It's binary, like the buffer, so
124
+ # a token with non-ASCII characters doesn't turn the buffer UTF-8.
125
+ token_suffix = routing_tokens(p_token: p_token, l_token: l_token).b
125
126
  ttl = integer_flag(:ttl, ttl) if ttl
126
127
 
127
128
  buffer = ''.b
@@ -136,11 +137,29 @@ module Dalli
136
137
  buffer << ' b' if base64
137
138
  buffer << " F#{bitflags}" if bitflags
138
139
  buffer << " T#{ttl}" if ttl
139
- buffer << ' MS q' << token_suffix << TERMINATOR << value << TERMINATOR
140
+ buffer << ' MS q' << token_suffix << TERMINATOR
141
+ append_bytes(buffer, value)
142
+ buffer << TERMINATOR
140
143
  end
141
144
  buffer << META_NOOP
142
145
  end
143
146
 
147
+ # Values are appended as bytes. One batch can hold UTF-8 and binary
148
+ # values, which String#<< refuses to combine, and a raw value can be in
149
+ # an encoding that isn't ASCII-compatible, such as UTF-16.
150
+ # Don't chain on the result: TruffleRuby's append_as_bytes returns its
151
+ # arguments rather than the buffer.
152
+ if String.method_defined?(:append_as_bytes)
153
+ def append_bytes(buffer, value)
154
+ buffer.append_as_bytes(value)
155
+ end
156
+ else
157
+ def append_bytes(buffer, value)
158
+ buffer << (value.encoding == Encoding::BINARY ? value : value.b)
159
+ end
160
+ end
161
+ private :append_bytes
162
+
144
163
  # Thundering herd protection flag:
145
164
  # - stale (I flag): Instead of deleting the item, mark it as stale. Other clients
146
165
  # using N/R flags will see the X flag and know the item is being regenerated.
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '5.2.4'
4
+ VERSION = '5.2.6'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.6.27'
7
7
  end
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.2.4
4
+ version: 5.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein