dalli 3.2.2 → 3.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 539a5ec7700d7d96bece70021727ff422d24202ab9e89fd07afc623dc3480502
4
- data.tar.gz: ba9231846477125fd3ed77f5a60f0df3445e3cda12c1736cd71f6b299213accb
3
+ metadata.gz: e182f80ca3d5c567c59e32d7396f0c347ad79633672af15d6f231950909f12dd
4
+ data.tar.gz: 92ddb5854da64ce59fb07c2eae7e1c1ce94d36868d26776c6415d20569dc0466
5
5
  SHA512:
6
- metadata.gz: 4039d6ce807dc60351f5781438ae84a0d2a147d5af567607653fcfb943ca3d3e0bcfd231cc67dbf4b761a5f0797ba4e5c99e9441d8d1e738eeb356805d139b89
7
- data.tar.gz: 5c31e6bc0d5b4b35280f7fd6747172838a8b037209d365906830634b32109a16c0301f732418c2ca99480ff3a37d9247a401c61e1d887ecf11a45dd4448d162b
6
+ metadata.gz: 89b2f797e3abe759ddb154c10c91cfe4126341c0bd95202637e8ff67845a02669e0be4cdfc486679e45322f31976b41a404f7223b03042107aab8db3011414c5
7
+ data.tar.gz: 4bc8eb1c4b478b946e202709d9d20995ebcb894ab32b5be070a19f99cecdfbd5ff0219344fe7288710202159a1b33298c1408e878a39cf256086f892ea5c4d41
@@ -4,10 +4,25 @@ Dalli Changelog
4
4
  Unreleased
5
5
  ==========
6
6
 
7
+ 3.2.4
8
+ ==========
9
+
10
+ - Cache PID calls for performance since glibc no longer caches in recent versions (casperisfine)
11
+ - Preallocate the read buffer in Socket#readfull (casperisfine)
12
+
13
+ 3.2.3
14
+ ==========
15
+
16
+ - Sanitize CAS inputs to ensure additional commands are not passed to memcached (xhzeem / petergoldstein)
17
+ - Sanitize input to flush command to ensure additional commands are not passed to memcached (xhzeem / petergoldstein)
18
+ - Namespaces passed as procs are now evaluated every time, as opposed to just on initialization (nrw505)
19
+ - Fix missing require of uri in ServerConfigParser (adam12)
20
+ - Fix link to the CHANGELOG.md file in README.md (rud)
21
+
7
22
  3.2.2
8
23
  ==========
9
24
 
10
- - Ensure apps are resilient against old session ids
25
+ - Ensure apps are resilient against old session ids (kbrock)
11
26
 
12
27
  3.2.1
13
28
  ==========
data/Gemfile CHANGED
@@ -4,6 +4,18 @@ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
+ group :development, :test do
8
+ gem 'connection_pool'
9
+ gem 'minitest', '~> 5'
10
+ gem 'rack', '~> 2.0', '>= 2.2.0'
11
+ gem 'rake', '~> 13.0'
12
+ gem 'rubocop'
13
+ gem 'rubocop-minitest'
14
+ gem 'rubocop-performance'
15
+ gem 'rubocop-rake'
16
+ gem 'simplecov'
17
+ end
18
+
7
19
  group :test do
8
20
  gem 'ruby-prof', platform: :mri
9
21
  end
data/README.md CHANGED
@@ -25,9 +25,15 @@ The name is a variant of Salvador Dali for his famous painting [The Persistence
25
25
  * [Forum](https://github.com/petergoldstein/dalli/discussions/categories/q-a) - If you have questions about Dalli, please post them here.
26
26
  * [Client API](https://rubydoc.info/github/petergoldstein/dalli/Dalli/Client) - Ruby documentation for the `Dalli::Client` API
27
27
 
28
+ ## Development
29
+
30
+ After checking out the repo, run `bin/setup` to install dependencies. You can run `bin/console` for an interactive prompt that will allow you to experiment.
31
+
32
+ To install this gem onto your local machine, run `bundle exec rake install`.
33
+
28
34
  ## Contributing
29
35
 
30
- If you have a fix you wish to provide, please fork the code, fix in your local project and then send a pull request on github. Please ensure that you include a test which verifies your fix and update `History.md` with a one sentence description of your fix so you get credit as a contributor.
36
+ If you have a fix you wish to provide, please fork the code, fix in your local project and then send a pull request on github. Please ensure that you include a test which verifies your fix and update the [changelog](CHANGELOG.md) with a one sentence description of your fix so you get credit as a contributor.
31
37
 
32
38
  ## Appreciation
33
39
 
@@ -61,7 +61,7 @@ module Dalli
61
61
  def key_with_namespace(key)
62
62
  return key if namespace.nil?
63
63
 
64
- "#{namespace}#{NAMESPACE_SEPARATOR}#{key}"
64
+ "#{evaluate_namespace}#{NAMESPACE_SEPARATOR}#{key}"
65
65
  end
66
66
 
67
67
  def key_without_namespace(key)
@@ -75,6 +75,8 @@ module Dalli
75
75
  end
76
76
 
77
77
  def namespace_regexp
78
+ return /\A#{Regexp.escape(evaluate_namespace)}:/ if namespace.is_a?(Proc)
79
+
78
80
  @namespace_regexp ||= /\A#{Regexp.escape(namespace)}:/.freeze unless namespace.nil?
79
81
  end
80
82
 
@@ -87,9 +89,15 @@ module Dalli
87
89
  def namespace_from_options
88
90
  raw_namespace = @key_options[:namespace]
89
91
  return nil unless raw_namespace
90
- return raw_namespace.call.to_s if raw_namespace.is_a?(Proc)
92
+ return raw_namespace.to_s unless raw_namespace.is_a?(Proc)
93
+
94
+ raw_namespace
95
+ end
96
+
97
+ def evaluate_namespace
98
+ return namespace.call.to_s if namespace.is_a?(Proc)
91
99
 
92
- raw_namespace.to_s
100
+ namespace
93
101
  end
94
102
 
95
103
  ##
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dalli
4
+ ##
5
+ # Dalli::PIDCache is a wrapper class for PID checking to avoid system calls when checking the PID.
6
+ ##
7
+ module PIDCache
8
+ if !Process.respond_to?(:fork) # JRuby or TruffleRuby
9
+ @pid = Process.pid
10
+ singleton_class.attr_reader(:pid)
11
+ elsif Process.respond_to?(:_fork) # Ruby 3.1+
12
+ class << self
13
+ attr_reader :pid
14
+
15
+ def update!
16
+ @pid = Process.pid
17
+ end
18
+ end
19
+ update!
20
+
21
+ ##
22
+ # Dalli::PIDCache::CoreExt hooks into Process to be able to reset the PID cache after fork
23
+ ##
24
+ module CoreExt
25
+ def _fork
26
+ child_pid = super
27
+ PIDCache.update! if child_pid.zero?
28
+ child_pid
29
+ end
30
+ end
31
+ Process.singleton_class.prepend(CoreExt)
32
+ else # Ruby 3.0 or older
33
+ class << self
34
+ def pid
35
+ Process.pid
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -167,7 +167,7 @@ module Dalli
167
167
  groups = @ring.keys_grouped_by_server(keys)
168
168
  if (unfound_keys = groups.delete(nil))
169
169
  Dalli.logger.debug do
170
- "unable to get keys for #{unfound_keys.length} keys "\
170
+ "unable to get keys for #{unfound_keys.length} keys " \
171
171
  'because no matching server was found'
172
172
  end
173
173
  end
@@ -197,8 +197,6 @@ module Dalli
197
197
  authenticate_connection if require_auth?
198
198
  @version = version # Connect socket if not authed
199
199
  up!
200
- rescue Dalli::DalliError
201
- raise
202
200
  end
203
201
 
204
202
  def pipelined_get(keys)
@@ -86,7 +86,7 @@ module Dalli
86
86
  touch: TTL_AND_KEY,
87
87
  gat: TTL_AND_KEY
88
88
  }.freeze
89
- FORMAT = BODY_FORMATS.transform_values { |v| REQ_HEADER_FORMAT + v; }
89
+ FORMAT = BODY_FORMATS.transform_values { |v| REQ_HEADER_FORMAT + v }
90
90
 
91
91
  # rubocop:disable Metrics/ParameterLists
92
92
  def self.standard_request(opkey:, key: nil, value: nil, opaque: 0, cas: 0, bitflags: nil, ttl: nil)
@@ -109,7 +109,7 @@ module Dalli
109
109
  end
110
110
 
111
111
  def self.as_8byte_uint(val)
112
- [val >> 32, 0xFFFFFFFF & val]
112
+ [val >> 32, val & 0xFFFFFFFF]
113
113
  end
114
114
  end
115
115
  end
@@ -50,7 +50,7 @@ module Dalli
50
50
  extra_len = resp_header.extra_len
51
51
  key_len = resp_header.key_len
52
52
  bitflags = extra_len.positive? ? body.unpack1('N') : 0x0
53
- key = body.byteslice(extra_len, key_len).force_encoding('UTF-8') if key_len.positive?
53
+ key = body.byteslice(extra_len, key_len).force_encoding(Encoding::UTF_8) if key_len.positive?
54
54
  value = body.byteslice((extra_len + key_len)..-1)
55
55
  value = parse_as_stored_value ? @value_marshaller.retrieve(value, bitflags) : value
56
56
  [key, value]
@@ -4,6 +4,8 @@ require 'English'
4
4
  require 'socket'
5
5
  require 'timeout'
6
6
 
7
+ require 'dalli/pid_cache'
8
+
7
9
  module Dalli
8
10
  module Protocol
9
11
  ##
@@ -51,7 +53,7 @@ module Dalli
51
53
  Dalli.logger.debug { "Dalli::Server#connect #{name}" }
52
54
 
53
55
  @sock = memcached_socket
54
- @pid = Process.pid
56
+ @pid = PIDCache.pid
55
57
  rescue SystemCallError, Timeout::Error, EOFError, SocketError => e
56
58
  # SocketError = DNS resolution failure
57
59
  error_on_request!(e)
@@ -226,7 +228,7 @@ module Dalli
226
228
  end
227
229
 
228
230
  def fork_detected?
229
- @pid && @pid != Process.pid
231
+ @pid && @pid != PIDCache.pid
230
232
  end
231
233
 
232
234
  def log_down_detected
@@ -23,7 +23,7 @@ module Dalli
23
23
  def self.decode(encoded_key, base64_encoded)
24
24
  return encoded_key unless base64_encoded
25
25
 
26
- Base64.strict_decode64(encoded_key).force_encoding('UTF-8')
26
+ Base64.strict_decode64(encoded_key).force_encoding(Encoding::UTF_8)
27
27
  end
28
28
  end
29
29
  end
@@ -31,7 +31,7 @@ module Dalli
31
31
  cmd << ' c' unless %i[append prepend].include?(mode)
32
32
  cmd << ' b' if base64
33
33
  cmd << " F#{bitflags}" if bitflags
34
- cmd << " C#{cas}" if cas && !cas.zero?
34
+ cmd << cas_string(cas)
35
35
  cmd << " T#{ttl}" if ttl
36
36
  cmd << " M#{mode_to_token(mode)}"
37
37
  cmd << ' q' if quiet
@@ -43,7 +43,7 @@ module Dalli
43
43
  def self.meta_delete(key:, cas: nil, ttl: nil, base64: false, quiet: false)
44
44
  cmd = "md #{key}"
45
45
  cmd << ' b' if base64
46
- cmd << " C#{cas}" if cas && !cas.zero?
46
+ cmd << cas_string(cas)
47
47
  cmd << " T#{ttl}" if ttl
48
48
  cmd << ' q' if quiet
49
49
  cmd + TERMINATOR
@@ -54,8 +54,9 @@ module Dalli
54
54
  cmd << ' b' if base64
55
55
  cmd << " D#{delta}" if delta
56
56
  cmd << " J#{initial}" if initial
57
- cmd << " C#{cas}" if cas && !cas.zero?
58
- cmd << " N#{ttl}" if ttl
57
+ # Always set a TTL if an initial value is specified
58
+ cmd << " N#{ttl || 0}" if ttl || initial
59
+ cmd << cas_string(cas)
59
60
  cmd << ' q' if quiet
60
61
  cmd << " M#{incr ? 'I' : 'D'}"
61
62
  cmd + TERMINATOR
@@ -75,7 +76,7 @@ module Dalli
75
76
 
76
77
  def self.flush(delay: nil, quiet: false)
77
78
  cmd = +'flush_all'
78
- cmd << " #{delay}" if delay
79
+ cmd << " #{parse_to_64_bit_int(delay, 0)}" if delay
79
80
  cmd << ' noreply' if quiet
80
81
  cmd + TERMINATOR
81
82
  end
@@ -102,6 +103,18 @@ module Dalli
102
103
  end
103
104
  end
104
105
  # rubocop:enable Metrics/MethodLength
106
+
107
+ def self.cas_string(cas)
108
+ cas = parse_to_64_bit_int(cas, nil)
109
+ cas.nil? || cas.zero? ? '' : " C#{cas}"
110
+ end
111
+
112
+ def self.parse_to_64_bit_int(val, default)
113
+ val.nil? ? nil : Integer(val)
114
+ rescue ArgumentError
115
+ # Sanitize to default if it isn't parsable as an integer
116
+ default
117
+ end
105
118
  end
106
119
  end
107
120
  end
@@ -44,6 +44,7 @@ module Dalli
44
44
  end
45
45
 
46
46
  def touch(key, ttl)
47
+ ttl = TtlSanitizer.sanitize(ttl)
47
48
  encoded_key, base64 = KeyRegularizer.encode(key)
48
49
  req = RequestFormatter.meta_get(key: encoded_key, ttl: ttl, value: false, base64: base64)
49
50
  write(req)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uri'
4
+
3
5
  module Dalli
4
6
  module Protocol
5
7
  ##
data/lib/dalli/socket.rb CHANGED
@@ -13,7 +13,7 @@ module Dalli
13
13
  ##
14
14
  module InstanceMethods
15
15
  def readfull(count)
16
- value = +''
16
+ value = String.new(capacity: count + 1)
17
17
  loop do
18
18
  result = read_nonblock(count - value.bytesize, exception: false)
19
19
  value << result if append_to_buffer?(result)
data/lib/dalli/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dalli
4
- VERSION = '3.2.2'
4
+ VERSION = '3.2.4'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.4'
7
7
  end
@@ -170,7 +170,7 @@ module Rack
170
170
  def ensure_connection_pool_added!
171
171
  require 'connection_pool'
172
172
  rescue LoadError => e
173
- warn "You don't have connection_pool installed in your application. "\
173
+ warn "You don't have connection_pool installed in your application. " \
174
174
  'Please add it to your Gemfile and run bundle install'
175
175
  raise e
176
176
  end
@@ -178,7 +178,7 @@ module Rack
178
178
  def with_dalli_client(result_on_error = nil, &block)
179
179
  @data.with(&block)
180
180
  rescue ::Dalli::DalliError, Errno::ECONNREFUSED
181
- raise if /undefined class/.match?($ERROR_INFO.message)
181
+ raise if $ERROR_INFO.message.include?('undefined class')
182
182
 
183
183
  if $VERBOSE
184
184
  warn "#{self} is unable to find memcached server."
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: 3.2.2
4
+ version: 3.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
@@ -9,140 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-06-15 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: connection_pool
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '0'
21
- type: :development
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '0'
28
- - !ruby/object:Gem::Dependency
29
- name: minitest
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '5'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '5'
42
- - !ruby/object:Gem::Dependency
43
- name: rack
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '2.0'
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- version: 2.2.0
52
- type: :development
53
- prerelease: false
54
- version_requirements: !ruby/object:Gem::Requirement
55
- requirements:
56
- - - "~>"
57
- - !ruby/object:Gem::Version
58
- version: '2.0'
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 2.2.0
62
- - !ruby/object:Gem::Dependency
63
- name: rake
64
- requirement: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '13.0'
69
- type: :development
70
- prerelease: false
71
- version_requirements: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '13.0'
76
- - !ruby/object:Gem::Dependency
77
- name: rubocop
78
- requirement: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- type: :development
84
- prerelease: false
85
- version_requirements: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- - !ruby/object:Gem::Dependency
91
- name: rubocop-minitest
92
- requirement: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- type: :development
98
- prerelease: false
99
- version_requirements: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- - !ruby/object:Gem::Dependency
105
- name: rubocop-performance
106
- requirement: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- type: :development
112
- prerelease: false
113
- version_requirements: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- - !ruby/object:Gem::Dependency
119
- name: rubocop-rake
120
- requirement: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- type: :development
126
- prerelease: false
127
- version_requirements: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- - !ruby/object:Gem::Dependency
133
- name: simplecov
134
- requirement: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- type: :development
140
- prerelease: false
141
- version_requirements: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
12
+ date: 2023-02-17 00:00:00.000000000 Z
13
+ dependencies: []
146
14
  description: High performance memcached client for Ruby
147
15
  email:
148
16
  - peter.m.goldstein@gmail.com
@@ -151,8 +19,8 @@ executables: []
151
19
  extensions: []
152
20
  extra_rdoc_files: []
153
21
  files:
22
+ - CHANGELOG.md
154
23
  - Gemfile
155
- - History.md
156
24
  - LICENSE
157
25
  - README.md
158
26
  - lib/dalli.rb
@@ -161,6 +29,7 @@ files:
161
29
  - lib/dalli/compressor.rb
162
30
  - lib/dalli/key_manager.rb
163
31
  - lib/dalli/options.rb
32
+ - lib/dalli/pid_cache.rb
164
33
  - lib/dalli/pipelined_getter.rb
165
34
  - lib/dalli/protocol.rb
166
35
  - lib/dalli/protocol/base.rb
@@ -206,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
75
  - !ruby/object:Gem::Version
207
76
  version: '0'
208
77
  requirements: []
209
- rubygems_version: 3.3.16
78
+ rubygems_version: 3.4.5
210
79
  signing_key:
211
80
  specification_version: 4
212
81
  summary: High performance memcached client for Ruby