dalli 3.2.3 → 3.2.5

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: f0ef39f4ff4e9e465b2522707f3c4eea85a94373ba0d3de4b9d940830001b118
4
- data.tar.gz: 35cb362ec6075818f062106769481e92c90752936ecaf9a00fae25725ee4ea94
3
+ metadata.gz: bc1fc3d1639a4e5380a972472d830cd6999f016672af936701201521433180e3
4
+ data.tar.gz: 154207a695751bf6430f597783a6382bcb764f42e6f71303b2ead07f90e90f24
5
5
  SHA512:
6
- metadata.gz: 0c6b2b205551fb10fb2e49ab7c3c3eba3c552c0bc4b8132783b51f6cdc7a9ddd4c272bd24ed13f9d78e29353e3b84daec001b2691ad630bdfd517bb7af86fd54
7
- data.tar.gz: 6ef9f6733d8e6d5f8a6bf8fb7d782e99c450494e03d4e5fe46037b77665682df63eaa9a3eb05007d1720b175564c8e91524c0df92d0856dc1f68066501732a06
6
+ metadata.gz: 4f7c65a4528f180ff8ccad3d39f0197cd83d409830e188e59fc8a03075e7a61c71c111db409cbefa01fb26d6e408fae2e5f2701e4f370744bf33a08023c33831
7
+ data.tar.gz: cbd13ba3453f17eb012632f5d077552eb6ea46adbb349f4fb72b612c8d9fe2bb6a9b64641a2d105592d0def88ad7b467a1f007ed6abb76e8c0fdb8d94064c369
data/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ Dalli Changelog
4
4
  Unreleased
5
5
  ==========
6
6
 
7
+
8
+ 3.2.5
9
+ ==========
10
+
11
+ - Better handle memcached requests being interrupted by Thread#raise or Thread#kill (byroot)
12
+ - Unexpected errors are no longer treated as `Dalli::NetworkError`, including errors raised by `Timeout.timeout` (byroot)
13
+
14
+ 3.2.4
15
+ ==========
16
+
17
+ - Cache PID calls for performance since glibc no longer caches in recent versions (byroot)
18
+ - Preallocate the read buffer in Socket#readfull (byroot)
19
+
7
20
  3.2.3
8
21
  ==========
9
22
 
@@ -45,7 +58,7 @@ Unreleased
45
58
  3.1.4
46
59
  ==========
47
60
 
48
- - Improve response parsing performance (casperisfine)
61
+ - Improve response parsing performance (byroot)
49
62
  - Reorganize binary protocol parsing a bit (petergoldstein)
50
63
  - Fix handling of non-ASCII keys in get_multi (petergoldstein)
51
64
 
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,6 +25,12 @@ 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
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.
@@ -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
@@ -32,7 +32,13 @@ module Dalli
32
32
  verify_state(opkey)
33
33
 
34
34
  begin
35
- send(opkey, *args)
35
+ @connection_manager.start_request!
36
+ response = send(opkey, *args)
37
+
38
+ # pipelined_get emit query but doesn't read the response(s)
39
+ @connection_manager.finish_request! unless opkey == :pipelined_get
40
+
41
+ response
36
42
  rescue Dalli::MarshalError => e
37
43
  log_marshal_err(args.first, e)
38
44
  raise
@@ -40,7 +46,8 @@ module Dalli
40
46
  raise
41
47
  rescue StandardError => e
42
48
  log_unexpected_err(e)
43
- down!
49
+ close
50
+ raise
44
51
  end
45
52
  end
46
53
 
@@ -65,10 +72,9 @@ module Dalli
65
72
  #
66
73
  # Returns nothing.
67
74
  def pipeline_response_setup
68
- verify_state(:getkq)
75
+ verify_pipelined_state(:getkq)
69
76
  write_noop
70
77
  response_buffer.reset
71
- @connection_manager.start_request!
72
78
  end
73
79
 
74
80
  # Attempt to receive and parse as many key/value pairs as possible
@@ -169,6 +175,11 @@ module Dalli
169
175
  raise_down_error unless ensure_connected!
170
176
  end
171
177
 
178
+ def verify_pipelined_state(_opkey)
179
+ @connection_manager.confirm_in_progress!
180
+ raise_down_error unless connected?
181
+ end
182
+
172
183
  # The socket connection to the underlying server is initialized as a side
173
184
  # effect of this call. In fact, this is the ONLY place where that
174
185
  # socket connection is initialized.
@@ -197,8 +208,6 @@ module Dalli
197
208
  authenticate_connection if require_auth?
198
209
  @version = version # Connect socket if not authed
199
210
  up!
200
- rescue Dalli::DalliError
201
- raise
202
211
  end
203
212
 
204
213
  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,8 @@ 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
57
+ @request_in_progress = false
55
58
  rescue SystemCallError, Timeout::Error, EOFError, SocketError => e
56
59
  # SocketError = DNS resolution failure
57
60
  error_on_request!(e)
@@ -96,7 +99,13 @@ module Dalli
96
99
  end
97
100
 
98
101
  def confirm_ready!
99
- error_on_request!(RuntimeError.new('Already writing to socket')) if request_in_progress?
102
+ close if request_in_progress?
103
+ close_on_fork if fork_detected?
104
+ end
105
+
106
+ def confirm_in_progress!
107
+ raise '[Dalli] No request in progress. This may be a bug in Dalli.' unless request_in_progress?
108
+
100
109
  close_on_fork if fork_detected?
101
110
  end
102
111
 
@@ -122,10 +131,14 @@ module Dalli
122
131
  end
123
132
 
124
133
  def start_request!
134
+ raise '[Dalli] Request already in progress. This may be a bug in Dalli.' if @request_in_progress
135
+
125
136
  @request_in_progress = true
126
137
  end
127
138
 
128
139
  def finish_request!
140
+ raise '[Dalli] No request in progress. This may be a bug in Dalli.' unless @request_in_progress
141
+
129
142
  @request_in_progress = false
130
143
  end
131
144
 
@@ -134,36 +147,26 @@ module Dalli
134
147
  end
135
148
 
136
149
  def read_line
137
- start_request!
138
150
  data = @sock.gets("\r\n")
139
151
  error_on_request!('EOF in read_line') if data.nil?
140
- finish_request!
141
152
  data
142
153
  rescue SystemCallError, Timeout::Error, EOFError => e
143
154
  error_on_request!(e)
144
155
  end
145
156
 
146
157
  def read(count)
147
- start_request!
148
- data = @sock.readfull(count)
149
- finish_request!
150
- data
158
+ @sock.readfull(count)
151
159
  rescue SystemCallError, Timeout::Error, EOFError => e
152
160
  error_on_request!(e)
153
161
  end
154
162
 
155
163
  def write(bytes)
156
- start_request!
157
- result = @sock.write(bytes)
158
- finish_request!
159
- result
164
+ @sock.write(bytes)
160
165
  rescue SystemCallError, Timeout::Error => e
161
166
  error_on_request!(e)
162
167
  end
163
168
 
164
- # Non-blocking read. Should only be used in the context
165
- # of a caller who has called start_request!, but not yet
166
- # called finish_request!. Here to support the operation
169
+ # Non-blocking read. Here to support the operation
167
170
  # of the get_multi operation
168
171
  def read_nonblock
169
172
  @sock.read_available
@@ -226,7 +229,7 @@ module Dalli
226
229
  end
227
230
 
228
231
  def fork_detected?
229
- @pid && @pid != Process.pid
232
+ @pid && @pid != PIDCache.pid
230
233
  end
231
234
 
232
235
  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
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.3'
4
+ VERSION = '3.2.5'
5
5
 
6
6
  MIN_SUPPORTED_MEMCACHED_VERSION = '1.4'
7
7
  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.3
4
+ version: 3.2.5
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-10-28 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-06-13 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
@@ -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.24
78
+ rubygems_version: 3.4.14
210
79
  signing_key:
211
80
  specification_version: 4
212
81
  summary: High performance memcached client for Ruby