dalli 5.1.6 → 5.1.8

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: a14acb855194ffb54b0395ba7e9a4785a69e3d00068088e42aca53d91dcc3929
4
- data.tar.gz: 3017450cd4da74f426885ae2684f562b42983ca83f85b13242611b7d32f76c02
3
+ metadata.gz: aedfdf5af128f44424f0d21689f66a86813b9714677716854889788fe3b62546
4
+ data.tar.gz: 4375bcb70c31f53ac8fbf13cc4bc16171756a87148f95c024d214d8028216795
5
5
  SHA512:
6
- metadata.gz: 0b6f8bd702bb10c9f4d47ad981fbc702ac00dda484a84f82449fb0874aae98127b96c8518aad7768c7996f8c95c2772394771e84128d0c4d65278257a28540c4
7
- data.tar.gz: 766ebee713280bc7ec98ec53becf3ece1a73c2734171f27379e13e2343f2dfce16649a77104254084cf9b948679646a8b6f4cbb0cb55529de354e1686d0e8b31
6
+ metadata.gz: ea2568b866c13f7d3bb9bf7d0c305c2164503df325c029d5aa160edb1316bc8ff01f97ec30d46e8a5113ed49350f6813c915f6202a093c7d28b21f9aed690cf0
7
+ data.tar.gz: 2dac25033ab69bc1819eae18a94e2f7b4b672e540438e94f6aa8d73fe414baa47b17d77a65137b667ee1a9d0c57927477e30563b34c08d1cf4dbfde5f760ca1f
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ Dalli Changelog
4
4
  Unreleased
5
5
  ==========
6
6
 
7
+ 5.1.8
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.1.7
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.1.6
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
@@ -162,7 +162,7 @@ module Dalli
162
162
  bitflags: bitflags, cas: cas,
163
163
  ttl: ttl, mode: mode, quiet: quiet,
164
164
  **routing_token_kwargs(options))
165
- write("#{req}#{value}#{TERMINATOR}")
165
+ write(request_with_value(req, value))
166
166
  @connection_manager.flush unless quiet
167
167
  end
168
168
  # rubocop:enable Metrics/ParameterLists
@@ -183,10 +183,20 @@ module Dalli
183
183
  req = RequestFormatter.meta_set(key: key, value: value,
184
184
  cas: cas, ttl: ttl, mode: mode, quiet: quiet?,
185
185
  **routing_token_kwargs(options))
186
- write("#{req}#{value}#{TERMINATOR}")
186
+ write(request_with_value(req, value))
187
187
  @connection_manager.flush unless quiet?
188
188
  end
189
189
 
190
+ # Ruby refuses to join Strings whose encodings clash, such as a raw
191
+ # UTF-16 value, or a routing token with non-ASCII characters next to a
192
+ # binary value. memcached stores bytes, so join the bytes then.
193
+ # Joining the Strings first keeps the common case free of extra checks.
194
+ def request_with_value(req, value)
195
+ "#{req}#{value}#{TERMINATOR}"
196
+ rescue Encoding::CompatibilityError
197
+ "#{req.b}#{value.b}#{TERMINATOR}"
198
+ end
199
+
190
200
  # Delete Commands
191
201
  #
192
202
  # `options` supports the meta-delete keys :invalidate, :tombstone_ttl and
@@ -107,8 +107,9 @@ module Dalli
107
107
 
108
108
  def multi_meta_set(entries, ttl: nil, p_token: nil, l_token: nil)
109
109
  # Routing tokens apply to every entry in the batch, so the suffix is
110
- # built once rather than per entry.
111
- token_suffix = routing_tokens(p_token: p_token, l_token: l_token)
110
+ # built once rather than per entry. It's binary, like the buffer, so
111
+ # a token with non-ASCII characters doesn't turn the buffer UTF-8.
112
+ token_suffix = routing_tokens(p_token: p_token, l_token: l_token).b
112
113
  ttl = integer_flag(:ttl, ttl) if ttl
113
114
 
114
115
  buffer = ''.b
@@ -123,11 +124,29 @@ module Dalli
123
124
  buffer << ' b' if base64
124
125
  buffer << " F#{bitflags}" if bitflags
125
126
  buffer << " T#{ttl}" if ttl
126
- buffer << ' MS q' << token_suffix << TERMINATOR << value << TERMINATOR
127
+ buffer << ' MS q' << token_suffix << TERMINATOR
128
+ append_bytes(buffer, value)
129
+ buffer << TERMINATOR
127
130
  end
128
131
  buffer << META_NOOP
129
132
  end
130
133
 
134
+ # Values are appended as bytes. One batch can hold UTF-8 and binary
135
+ # values, which String#<< refuses to combine, and a raw value can be in
136
+ # an encoding that isn't ASCII-compatible, such as UTF-16.
137
+ # Don't chain on the result: TruffleRuby's append_as_bytes returns its
138
+ # arguments rather than the buffer.
139
+ if String.method_defined?(:append_as_bytes)
140
+ def append_bytes(buffer, value)
141
+ buffer.append_as_bytes(value)
142
+ end
143
+ else
144
+ def append_bytes(buffer, value)
145
+ buffer << (value.encoding == Encoding::BINARY ? value : value.b)
146
+ end
147
+ end
148
+ private :append_bytes
149
+
131
150
  # Thundering herd protection flag:
132
151
  # - stale (I flag): Instead of deleting the item, mark it as stale. Other clients
133
152
  # 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.1.6'
4
+ VERSION = '5.1.8'
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.1.6
4
+ version: 5.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein