dalli 3.1.2 → 3.1.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +5 -0
- data/lib/dalli/protocol/base.rb +238 -0
- data/lib/dalli/protocol/binary/response_processor.rb +8 -0
- data/lib/dalli/protocol/binary/sasl_authentication.rb +1 -1
- data/lib/dalli/protocol/binary.rb +24 -238
- 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: '0905b56adf194401de7755ab22ebef6b57f6e673e8332f7cb99b3ef9ed1dde63'
|
4
|
+
data.tar.gz: dac2014c748ef0c55fe171af0cec8dec807ae2468f758338c8968135acd47b92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85d8382dfd13c2353a61c74525e17bd81e000308e8d476851ed25fdb6ef81e9aff4276eb0b1484e30c83001301a3500140cb46a595081e1b21f0801ea32d7e28
|
7
|
+
data.tar.gz: 7f0adfc09e31d435bbb3f6cff2f65392a58f81697e7025ca71d9423f9a536d9c984ed7ba0742d0c0f5de0e865a7ebd2dc5548a0406c86d7f1fe0c8135c7af363
|
data/History.md
CHANGED
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
module Dalli
|
8
|
+
module Protocol
|
9
|
+
##
|
10
|
+
# Base class for a single Memcached server, containing logic common to all
|
11
|
+
# protocols. Contains logic for managing connection state to the server and value
|
12
|
+
# handling.
|
13
|
+
##
|
14
|
+
class Base
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
attr_accessor :weight, :options
|
18
|
+
|
19
|
+
def_delegators :@value_marshaller, :serializer, :compressor, :compression_min_size, :compress_by_default?
|
20
|
+
def_delegators :@connection_manager, :name, :sock, :hostname, :port, :close, :connected?, :socket_timeout,
|
21
|
+
:socket_type, :up!, :down!, :write, :reconnect_down_server?, :raise_down_error
|
22
|
+
|
23
|
+
def initialize(attribs, client_options = {})
|
24
|
+
hostname, port, socket_type, @weight, user_creds = ServerConfigParser.parse(attribs)
|
25
|
+
@options = client_options.merge(user_creds)
|
26
|
+
@value_marshaller = ValueMarshaller.new(@options)
|
27
|
+
@connection_manager = ConnectionManager.new(hostname, port, socket_type, @options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Chokepoint method for error handling and ensuring liveness
|
31
|
+
def request(opkey, *args)
|
32
|
+
verify_state(opkey)
|
33
|
+
|
34
|
+
begin
|
35
|
+
send(opkey, *args)
|
36
|
+
rescue Dalli::MarshalError => e
|
37
|
+
log_marshal_err(args.first, e)
|
38
|
+
raise
|
39
|
+
rescue Dalli::DalliError
|
40
|
+
raise
|
41
|
+
rescue StandardError => e
|
42
|
+
log_unexpected_err(e)
|
43
|
+
down!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Boolean method used by clients of this class to determine if this
|
49
|
+
# particular memcached instance is available for use.
|
50
|
+
def alive?
|
51
|
+
ensure_connected!
|
52
|
+
rescue Dalli::NetworkError
|
53
|
+
# ensure_connected! raises a NetworkError if connection fails. We
|
54
|
+
# want to capture that error and convert it to a boolean value here.
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def lock!; end
|
59
|
+
|
60
|
+
def unlock!; end
|
61
|
+
|
62
|
+
# Start reading key/value pairs from this connection. This is usually called
|
63
|
+
# after a series of GETKQ commands. A NOOP is sent, and the server begins
|
64
|
+
# flushing responses for kv pairs that were found.
|
65
|
+
#
|
66
|
+
# Returns nothing.
|
67
|
+
def pipeline_response_setup
|
68
|
+
verify_state(:getkq)
|
69
|
+
write_noop
|
70
|
+
response_buffer.reset
|
71
|
+
@connection_manager.start_request!
|
72
|
+
end
|
73
|
+
|
74
|
+
# Attempt to receive and parse as many key/value pairs as possible
|
75
|
+
# from this server. After #pipeline_response_setup, this should be invoked
|
76
|
+
# repeatedly whenever this server's socket is readable until
|
77
|
+
# #pipeline_complete?.
|
78
|
+
#
|
79
|
+
# Returns a Hash of kv pairs received.
|
80
|
+
def pipeline_next_responses
|
81
|
+
reconnect_on_pipeline_complete!
|
82
|
+
values = {}
|
83
|
+
|
84
|
+
response_buffer.read
|
85
|
+
|
86
|
+
resp_header, key, value = pipeline_response
|
87
|
+
# resp_header is not nil only if we have a full response to parse
|
88
|
+
# in the buffer
|
89
|
+
while resp_header
|
90
|
+
# If the status is ok and key is nil, then this is the response
|
91
|
+
# to the noop at the end of the pipeline
|
92
|
+
finish_pipeline && break if resp_header.ok? && key.nil?
|
93
|
+
|
94
|
+
# If the status is ok and the key is not nil, then this is a
|
95
|
+
# getkq response with a value that we want to set in the response hash
|
96
|
+
values[key] = [value, resp_header.cas] unless key.nil?
|
97
|
+
|
98
|
+
# Get the next response from the buffer
|
99
|
+
resp_header, key, value = pipeline_response
|
100
|
+
end
|
101
|
+
|
102
|
+
values
|
103
|
+
rescue SystemCallError, Timeout::Error, EOFError => e
|
104
|
+
@connection_manager.error_on_request!(e)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Abort current pipelined get. Generally used to signal an external
|
108
|
+
# timeout during pipelined get. The underlying socket is
|
109
|
+
# disconnected, and the exception is swallowed.
|
110
|
+
#
|
111
|
+
# Returns nothing.
|
112
|
+
def pipeline_abort
|
113
|
+
response_buffer.clear
|
114
|
+
@connection_manager.abort_request!
|
115
|
+
return true unless connected?
|
116
|
+
|
117
|
+
# Closes the connection, which ensures that our connection
|
118
|
+
# is in a clean state for future requests
|
119
|
+
@connection_manager.error_on_request!('External timeout')
|
120
|
+
rescue NetworkError
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
# Did the last call to #pipeline_response_setup complete successfully?
|
125
|
+
def pipeline_complete?
|
126
|
+
!response_buffer.in_progress?
|
127
|
+
end
|
128
|
+
|
129
|
+
def username
|
130
|
+
@options[:username] || ENV['MEMCACHE_USERNAME']
|
131
|
+
end
|
132
|
+
|
133
|
+
def password
|
134
|
+
@options[:password] || ENV['MEMCACHE_PASSWORD']
|
135
|
+
end
|
136
|
+
|
137
|
+
def require_auth?
|
138
|
+
!username.nil?
|
139
|
+
end
|
140
|
+
|
141
|
+
def quiet?
|
142
|
+
Thread.current[::Dalli::QUIET]
|
143
|
+
end
|
144
|
+
alias multi? quiet?
|
145
|
+
|
146
|
+
# NOTE: Additional public methods should be overridden in Dalli::Threadsafe
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
##
|
151
|
+
# Checks to see if we can execute the specified operation. Checks
|
152
|
+
# whether the connection is in use, and whether the command is allowed
|
153
|
+
##
|
154
|
+
def verify_state(opkey)
|
155
|
+
@connection_manager.confirm_ready!
|
156
|
+
verify_allowed_quiet!(opkey) if quiet?
|
157
|
+
|
158
|
+
# The ensure_connected call has the side effect of connecting the
|
159
|
+
# underlying socket if it is not connected, or there's been a disconnect
|
160
|
+
# because of timeout or other error. Method raises an error
|
161
|
+
# if it can't connect
|
162
|
+
raise_down_error unless ensure_connected!
|
163
|
+
end
|
164
|
+
|
165
|
+
# The socket connection to the underlying server is initialized as a side
|
166
|
+
# effect of this call. In fact, this is the ONLY place where that
|
167
|
+
# socket connection is initialized.
|
168
|
+
#
|
169
|
+
# Both this method and connect need to be in this class so we can do auth
|
170
|
+
# as required
|
171
|
+
#
|
172
|
+
# Since this is invoked exclusively in verify_state!, we don't need to worry about
|
173
|
+
# thread safety. Using it elsewhere may require revisiting that assumption.
|
174
|
+
def ensure_connected!
|
175
|
+
return true if connected?
|
176
|
+
return false unless reconnect_down_server?
|
177
|
+
|
178
|
+
connect # This call needs to be in this class so we can do auth
|
179
|
+
connected?
|
180
|
+
end
|
181
|
+
|
182
|
+
def cache_nils?(opts)
|
183
|
+
return false unless opts.is_a?(Hash)
|
184
|
+
|
185
|
+
opts[:cache_nils] ? true : false
|
186
|
+
end
|
187
|
+
|
188
|
+
def connect
|
189
|
+
@connection_manager.establish_connection
|
190
|
+
authenticate_connection if require_auth?
|
191
|
+
@version = version # Connect socket if not authed
|
192
|
+
up!
|
193
|
+
rescue Dalli::DalliError
|
194
|
+
raise
|
195
|
+
end
|
196
|
+
|
197
|
+
def pipelined_get(keys)
|
198
|
+
req = +''
|
199
|
+
keys.each do |key|
|
200
|
+
req << quiet_get_request(key)
|
201
|
+
end
|
202
|
+
# Could send noop here instead of in pipeline_response_setup
|
203
|
+
write(req)
|
204
|
+
end
|
205
|
+
|
206
|
+
def response_buffer
|
207
|
+
@response_buffer ||= ResponseBuffer.new(@connection_manager, response_processor)
|
208
|
+
end
|
209
|
+
|
210
|
+
def pipeline_response
|
211
|
+
response_buffer.process_single_getk_response
|
212
|
+
end
|
213
|
+
|
214
|
+
# Called after the noop response is received at the end of a set
|
215
|
+
# of pipelined gets
|
216
|
+
def finish_pipeline
|
217
|
+
response_buffer.clear
|
218
|
+
@connection_manager.finish_request!
|
219
|
+
|
220
|
+
true # to simplify response
|
221
|
+
end
|
222
|
+
|
223
|
+
def reconnect_on_pipeline_complete!
|
224
|
+
@connection_manager.reconnect! 'pipelined get has completed' if pipeline_complete?
|
225
|
+
end
|
226
|
+
|
227
|
+
def log_marshal_err(key, err)
|
228
|
+
Dalli.logger.error "Marshalling error for key '#{key}': #{err.message}"
|
229
|
+
Dalli.logger.error 'You are trying to cache a Ruby object which cannot be serialized to memcached.'
|
230
|
+
end
|
231
|
+
|
232
|
+
def log_unexpected_err(err)
|
233
|
+
Dalli.logger.error "Unexpected exception during Dalli request: #{err.class.name}: #{err.message}"
|
234
|
+
Dalli.logger.error err.backtrace.join("\n\t")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -89,6 +89,14 @@ module Dalli
|
|
89
89
|
resp_header.cas
|
90
90
|
end
|
91
91
|
|
92
|
+
def delete_response
|
93
|
+
resp_header, = read_response
|
94
|
+
return false if resp_header.not_found? || resp_header.not_stored?
|
95
|
+
|
96
|
+
raise_on_not_ok!(resp_header)
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
92
100
|
def no_body_response
|
93
101
|
resp_header, = read_response
|
94
102
|
return false if resp_header.not_stored? # Not stored, possible status for append/prepend
|
@@ -10,7 +10,7 @@ module Dalli
|
|
10
10
|
def perform_auth_negotiation
|
11
11
|
write(RequestFormatter.standard_request(opkey: :auth_negotiation))
|
12
12
|
|
13
|
-
status, content =
|
13
|
+
status, content = response_processor.auth_response
|
14
14
|
return [status, []] if content.nil?
|
15
15
|
|
16
16
|
# Substitute spaces for the \x00 returned by
|
@@ -4,11 +4,6 @@ require 'forwardable'
|
|
4
4
|
require 'socket'
|
5
5
|
require 'timeout'
|
6
6
|
|
7
|
-
require_relative 'binary/request_formatter'
|
8
|
-
require_relative 'binary/response_header'
|
9
|
-
require_relative 'binary/response_processor'
|
10
|
-
require_relative 'binary/sasl_authentication'
|
11
|
-
|
12
7
|
module Dalli
|
13
8
|
module Protocol
|
14
9
|
##
|
@@ -16,175 +11,13 @@ module Dalli
|
|
16
11
|
# protocol. Contains logic for managing connection state to the server (retries, etc),
|
17
12
|
# formatting requests to the server, and unpacking responses.
|
18
13
|
##
|
19
|
-
class Binary
|
20
|
-
|
21
|
-
|
22
|
-
attr_accessor :weight, :options
|
23
|
-
|
24
|
-
def_delegators :@value_marshaller, :serializer, :compressor, :compression_min_size, :compress_by_default?
|
25
|
-
def_delegators :@connection_manager, :name, :sock, :hostname, :port, :close, :connected?, :socket_timeout,
|
26
|
-
:socket_type, :up!, :down!, :write, :reconnect_down_server?, :raise_down_error
|
27
|
-
|
28
|
-
def initialize(attribs, client_options = {})
|
29
|
-
hostname, port, socket_type, @weight, user_creds = ServerConfigParser.parse(attribs)
|
30
|
-
@options = client_options.merge(user_creds)
|
31
|
-
@value_marshaller = ValueMarshaller.new(@options)
|
32
|
-
@connection_manager = ConnectionManager.new(hostname, port, socket_type, @options)
|
33
|
-
@response_processor = ResponseProcessor.new(@connection_manager, @value_marshaller)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Chokepoint method for error handling and ensuring liveness
|
37
|
-
def request(opkey, *args)
|
38
|
-
verify_state(opkey)
|
39
|
-
|
40
|
-
begin
|
41
|
-
send(opkey, *args)
|
42
|
-
rescue Dalli::MarshalError => e
|
43
|
-
log_marshal_err(args.first, e)
|
44
|
-
raise
|
45
|
-
rescue Dalli::DalliError
|
46
|
-
raise
|
47
|
-
rescue StandardError => e
|
48
|
-
log_unexpected_err(e)
|
49
|
-
down!
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
##
|
54
|
-
# Boolean method used by clients of this class to determine if this
|
55
|
-
# particular memcached instance is available for use.
|
56
|
-
def alive?
|
57
|
-
ensure_connected!
|
58
|
-
rescue Dalli::NetworkError
|
59
|
-
# ensure_connected! raises a NetworkError if connection fails. We
|
60
|
-
# want to capture that error and convert it to a boolean value here.
|
61
|
-
false
|
62
|
-
end
|
63
|
-
|
64
|
-
def lock!; end
|
65
|
-
|
66
|
-
def unlock!; end
|
67
|
-
|
68
|
-
# Start reading key/value pairs from this connection. This is usually called
|
69
|
-
# after a series of GETKQ commands. A NOOP is sent, and the server begins
|
70
|
-
# flushing responses for kv pairs that were found.
|
71
|
-
#
|
72
|
-
# Returns nothing.
|
73
|
-
def pipeline_response_setup
|
74
|
-
verify_state(:getkq)
|
75
|
-
write_noop
|
76
|
-
response_buffer.reset
|
77
|
-
@connection_manager.start_request!
|
78
|
-
end
|
79
|
-
|
80
|
-
# Attempt to receive and parse as many key/value pairs as possible
|
81
|
-
# from this server. After #pipeline_response_setup, this should be invoked
|
82
|
-
# repeatedly whenever this server's socket is readable until
|
83
|
-
# #pipeline_complete?.
|
84
|
-
#
|
85
|
-
# Returns a Hash of kv pairs received.
|
86
|
-
def pipeline_next_responses
|
87
|
-
reconnect_on_pipeline_complete!
|
88
|
-
values = {}
|
89
|
-
|
90
|
-
response_buffer.read
|
91
|
-
|
92
|
-
resp_header, key, value = pipeline_response
|
93
|
-
# resp_header is not nil only if we have a full response to parse
|
94
|
-
# in the buffer
|
95
|
-
while resp_header
|
96
|
-
# If the status is ok and key is nil, then this is the response
|
97
|
-
# to the noop at the end of the pipeline
|
98
|
-
finish_pipeline && break if resp_header.ok? && key.nil?
|
99
|
-
|
100
|
-
# If the status is ok and the key is not nil, then this is a
|
101
|
-
# getkq response with a value that we want to set in the response hash
|
102
|
-
values[key] = [value, resp_header.cas] unless key.nil?
|
103
|
-
|
104
|
-
# Get the next response from the buffer
|
105
|
-
resp_header, key, value = pipeline_response
|
106
|
-
end
|
107
|
-
|
108
|
-
values
|
109
|
-
rescue SystemCallError, Timeout::Error, EOFError => e
|
110
|
-
@connection_manager.error_on_request!(e)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Abort current pipelined get. Generally used to signal an external
|
114
|
-
# timeout during pipelined get. The underlying socket is
|
115
|
-
# disconnected, and the exception is swallowed.
|
116
|
-
#
|
117
|
-
# Returns nothing.
|
118
|
-
def pipeline_abort
|
119
|
-
response_buffer.clear
|
120
|
-
@connection_manager.abort_request!
|
121
|
-
return true unless connected?
|
122
|
-
|
123
|
-
# Closes the connection, which ensures that our connection
|
124
|
-
# is in a clean state for future requests
|
125
|
-
@connection_manager.error_on_request!('External timeout')
|
126
|
-
rescue NetworkError
|
127
|
-
true
|
128
|
-
end
|
129
|
-
|
130
|
-
# Did the last call to #pipeline_response_setup complete successfully?
|
131
|
-
def pipeline_complete?
|
132
|
-
!response_buffer.in_progress?
|
133
|
-
end
|
134
|
-
|
135
|
-
def username
|
136
|
-
@options[:username] || ENV['MEMCACHE_USERNAME']
|
137
|
-
end
|
138
|
-
|
139
|
-
def password
|
140
|
-
@options[:password] || ENV['MEMCACHE_PASSWORD']
|
141
|
-
end
|
142
|
-
|
143
|
-
def require_auth?
|
144
|
-
!username.nil?
|
14
|
+
class Binary < Base
|
15
|
+
def response_processor
|
16
|
+
@response_processor ||= ResponseProcessor.new(@connection_manager, @value_marshaller)
|
145
17
|
end
|
146
18
|
|
147
|
-
def quiet?
|
148
|
-
Thread.current[::Dalli::QUIET]
|
149
|
-
end
|
150
|
-
alias multi? quiet?
|
151
|
-
|
152
|
-
# NOTE: Additional public methods should be overridden in Dalli::Threadsafe
|
153
|
-
|
154
19
|
private
|
155
20
|
|
156
|
-
##
|
157
|
-
# Checks to see if we can execute the specified operation. Checks
|
158
|
-
# whether the connection is in use, and whether the command is allowed
|
159
|
-
##
|
160
|
-
def verify_state(opkey)
|
161
|
-
@connection_manager.confirm_ready!
|
162
|
-
verify_allowed_quiet!(opkey) if quiet?
|
163
|
-
|
164
|
-
# The ensure_connected call has the side effect of connecting the
|
165
|
-
# underlying socket if it is not connected, or there's been a disconnect
|
166
|
-
# because of timeout or other error. Method raises an error
|
167
|
-
# if it can't connect
|
168
|
-
raise_down_error unless ensure_connected!
|
169
|
-
end
|
170
|
-
|
171
|
-
# The socket connection to the underlying server is initialized as a side
|
172
|
-
# effect of this call. In fact, this is the ONLY place where that
|
173
|
-
# socket connection is initialized.
|
174
|
-
#
|
175
|
-
# Both this method and connect need to be in this class so we can do auth
|
176
|
-
# as required
|
177
|
-
#
|
178
|
-
# Since this is invoked exclusively in verify_state!, we don't need to worry about
|
179
|
-
# thread safety. Using it elsewhere may require revisiting that assumption.
|
180
|
-
def ensure_connected!
|
181
|
-
return true if connected?
|
182
|
-
return false unless reconnect_down_server?
|
183
|
-
|
184
|
-
connect # This call needs to be in this class so we can do auth
|
185
|
-
connected?
|
186
|
-
end
|
187
|
-
|
188
21
|
ALLOWED_QUIET_OPS = %i[add replace set delete incr decr append prepend flush noop].freeze
|
189
22
|
def verify_allowed_quiet!(opkey)
|
190
23
|
return if ALLOWED_QUIET_OPS.include?(opkey)
|
@@ -192,30 +25,28 @@ module Dalli
|
|
192
25
|
raise Dalli::NotPermittedMultiOpError, "The operation #{opkey} is not allowed in a quiet block."
|
193
26
|
end
|
194
27
|
|
195
|
-
def cache_nils?(opts)
|
196
|
-
return false unless opts.is_a?(Hash)
|
197
|
-
|
198
|
-
opts[:cache_nils] ? true : false
|
199
|
-
end
|
200
|
-
|
201
28
|
# Retrieval Commands
|
202
29
|
def get(key, options = nil)
|
203
30
|
req = RequestFormatter.standard_request(opkey: :get, key: key)
|
204
31
|
write(req)
|
205
|
-
|
32
|
+
response_processor.generic_response(unpack: true, cache_nils: cache_nils?(options))
|
33
|
+
end
|
34
|
+
|
35
|
+
def quiet_get_request(key)
|
36
|
+
RequestFormatter.standard_request(opkey: :getkq, key: key)
|
206
37
|
end
|
207
38
|
|
208
39
|
def gat(key, ttl, options = nil)
|
209
40
|
ttl = TtlSanitizer.sanitize(ttl)
|
210
41
|
req = RequestFormatter.standard_request(opkey: :gat, key: key, ttl: ttl)
|
211
42
|
write(req)
|
212
|
-
|
43
|
+
response_processor.generic_response(unpack: true, cache_nils: cache_nils?(options))
|
213
44
|
end
|
214
45
|
|
215
46
|
def touch(key, ttl)
|
216
47
|
ttl = TtlSanitizer.sanitize(ttl)
|
217
48
|
write(RequestFormatter.standard_request(opkey: :touch, key: key, ttl: ttl))
|
218
|
-
|
49
|
+
response_processor.generic_response
|
219
50
|
end
|
220
51
|
|
221
52
|
# TODO: This is confusing, as there's a cas command in memcached
|
@@ -223,7 +54,7 @@ module Dalli
|
|
223
54
|
def cas(key)
|
224
55
|
req = RequestFormatter.standard_request(opkey: :get, key: key)
|
225
56
|
write(req)
|
226
|
-
|
57
|
+
response_processor.data_cas_response
|
227
58
|
end
|
228
59
|
|
229
60
|
# Storage Commands
|
@@ -251,7 +82,7 @@ module Dalli
|
|
251
82
|
value: value, bitflags: bitflags,
|
252
83
|
ttl: ttl, cas: cas)
|
253
84
|
write(req)
|
254
|
-
|
85
|
+
response_processor.storage_response unless quiet?
|
255
86
|
end
|
256
87
|
# rubocop:enable Metrics/ParameterLists
|
257
88
|
|
@@ -267,7 +98,7 @@ module Dalli
|
|
267
98
|
|
268
99
|
def write_append_prepend(opkey, key, value)
|
269
100
|
write(RequestFormatter.standard_request(opkey: opkey, key: key, value: value))
|
270
|
-
|
101
|
+
response_processor.no_body_response unless quiet?
|
271
102
|
end
|
272
103
|
|
273
104
|
# Delete Commands
|
@@ -275,7 +106,7 @@ module Dalli
|
|
275
106
|
opkey = quiet? ? :deleteq : :delete
|
276
107
|
req = RequestFormatter.standard_request(opkey: opkey, key: key, cas: cas)
|
277
108
|
write(req)
|
278
|
-
|
109
|
+
response_processor.delete_response unless quiet?
|
279
110
|
end
|
280
111
|
|
281
112
|
# Arithmetic Commands
|
@@ -301,37 +132,37 @@ module Dalli
|
|
301
132
|
initial ||= 0
|
302
133
|
write(RequestFormatter.decr_incr_request(opkey: opkey, key: key,
|
303
134
|
count: count, initial: initial, expiry: expiry))
|
304
|
-
|
135
|
+
response_processor.decr_incr_response unless quiet?
|
305
136
|
end
|
306
137
|
|
307
138
|
# Other Commands
|
308
139
|
def flush(ttl = 0)
|
309
140
|
opkey = quiet? ? :flushq : :flush
|
310
141
|
write(RequestFormatter.standard_request(opkey: opkey, ttl: ttl))
|
311
|
-
|
142
|
+
response_processor.no_body_response unless quiet?
|
312
143
|
end
|
313
144
|
|
314
145
|
# Noop is a keepalive operation but also used to demarcate the end of a set of pipelined commands.
|
315
146
|
# We need to read all the responses at once.
|
316
147
|
def noop
|
317
148
|
write_noop
|
318
|
-
|
149
|
+
response_processor.multi_with_keys_response
|
319
150
|
end
|
320
151
|
|
321
152
|
def stats(info = '')
|
322
153
|
req = RequestFormatter.standard_request(opkey: :stat, key: info)
|
323
154
|
write(req)
|
324
|
-
|
155
|
+
response_processor.multi_with_keys_response
|
325
156
|
end
|
326
157
|
|
327
158
|
def reset_stats
|
328
159
|
write(RequestFormatter.standard_request(opkey: :stat, key: 'reset'))
|
329
|
-
|
160
|
+
response_processor.generic_response
|
330
161
|
end
|
331
162
|
|
332
163
|
def version
|
333
164
|
write(RequestFormatter.standard_request(opkey: :version))
|
334
|
-
|
165
|
+
response_processor.generic_response
|
335
166
|
end
|
336
167
|
|
337
168
|
def write_noop
|
@@ -339,55 +170,10 @@ module Dalli
|
|
339
170
|
write(req)
|
340
171
|
end
|
341
172
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
up!
|
347
|
-
rescue Dalli::DalliError
|
348
|
-
raise
|
349
|
-
end
|
350
|
-
|
351
|
-
def pipelined_get(keys)
|
352
|
-
req = +''
|
353
|
-
keys.each do |key|
|
354
|
-
req << RequestFormatter.standard_request(opkey: :getkq, key: key)
|
355
|
-
end
|
356
|
-
# Could send noop here instead of in pipeline_response_setup
|
357
|
-
write(req)
|
358
|
-
end
|
359
|
-
|
360
|
-
def response_buffer
|
361
|
-
@response_buffer ||= ResponseBuffer.new(@connection_manager, @response_processor)
|
362
|
-
end
|
363
|
-
|
364
|
-
def pipeline_response
|
365
|
-
response_buffer.process_single_getk_response
|
366
|
-
end
|
367
|
-
|
368
|
-
# Called after the noop response is received at the end of a set
|
369
|
-
# of pipelined gets
|
370
|
-
def finish_pipeline
|
371
|
-
response_buffer.clear
|
372
|
-
@connection_manager.finish_request!
|
373
|
-
|
374
|
-
true # to simplify response
|
375
|
-
end
|
376
|
-
|
377
|
-
def reconnect_on_pipeline_complete!
|
378
|
-
@connection_manager.reconnect! 'pipelined get has completed' if pipeline_complete?
|
379
|
-
end
|
380
|
-
|
381
|
-
def log_marshal_err(key, err)
|
382
|
-
Dalli.logger.error "Marshalling error for key '#{key}': #{err.message}"
|
383
|
-
Dalli.logger.error 'You are trying to cache a Ruby object which cannot be serialized to memcached.'
|
384
|
-
end
|
385
|
-
|
386
|
-
def log_unexpected_err(err)
|
387
|
-
Dalli.logger.error "Unexpected exception during Dalli request: #{err.class.name}: #{err.message}"
|
388
|
-
Dalli.logger.error err.backtrace.join("\n\t")
|
389
|
-
end
|
390
|
-
|
173
|
+
require_relative 'binary/request_formatter'
|
174
|
+
require_relative 'binary/response_header'
|
175
|
+
require_relative 'binary/response_processor'
|
176
|
+
require_relative 'binary/sasl_authentication'
|
391
177
|
include SaslAuthentication
|
392
178
|
end
|
393
179
|
end
|
data/lib/dalli/version.rb
CHANGED
data/lib/dalli.rb
CHANGED
@@ -62,6 +62,7 @@ require_relative 'dalli/key_manager'
|
|
62
62
|
require_relative 'dalli/pipelined_getter'
|
63
63
|
require_relative 'dalli/ring'
|
64
64
|
require_relative 'dalli/protocol'
|
65
|
+
require_relative 'dalli/protocol/base'
|
65
66
|
require_relative 'dalli/protocol/binary'
|
66
67
|
require_relative 'dalli/protocol/connection_manager'
|
67
68
|
require_relative 'dalli/protocol/response_buffer'
|
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.1.
|
4
|
+
version: 3.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter M. Goldstein
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-12-
|
12
|
+
date: 2021-12-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: connection_pool
|
@@ -121,6 +121,7 @@ files:
|
|
121
121
|
- lib/dalli/options.rb
|
122
122
|
- lib/dalli/pipelined_getter.rb
|
123
123
|
- lib/dalli/protocol.rb
|
124
|
+
- lib/dalli/protocol/base.rb
|
124
125
|
- lib/dalli/protocol/binary.rb
|
125
126
|
- lib/dalli/protocol/binary/request_formatter.rb
|
126
127
|
- lib/dalli/protocol/binary/response_header.rb
|