dalli 3.1.0 → 3.1.1

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.

@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'English'
4
3
  require 'forwardable'
5
4
  require 'socket'
6
5
  require 'timeout'
@@ -20,62 +19,30 @@ module Dalli
20
19
  class Binary
21
20
  extend Forwardable
22
21
 
23
- attr_accessor :hostname, :port, :weight, :options
24
- attr_reader :sock, :socket_type
22
+ attr_accessor :weight, :options
25
23
 
26
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
27
 
28
- DEFAULTS = {
29
- # seconds between trying to contact a remote server
30
- down_retry_delay: 30,
31
- # connect/read/write timeout for socket operations
32
- socket_timeout: 1,
33
- # times a socket operation may fail before considering the server dead
34
- socket_max_failures: 2,
35
- # amount of time to sleep between retries when a failure occurs
36
- socket_failure_delay: 0.1
37
- }.freeze
38
-
39
- def initialize(attribs, options = {})
40
- @hostname, @port, @weight, @socket_type, options = ServerConfigParser.parse(attribs, options)
41
- @options = DEFAULTS.merge(options)
28
+ def initialize(attribs, client_options = {})
29
+ hostname, port, socket_type, @weight, user_creds = ServerConfigParser.parse(attribs)
30
+ @options = client_options.merge(user_creds)
42
31
  @value_marshaller = ValueMarshaller.new(@options)
43
- @response_processor = ResponseProcessor.new(self, @value_marshaller)
44
- @response_buffer = ResponseBuffer.new(self, @response_processor)
45
-
46
- reset_down_info
47
- @sock = nil
48
- @pid = nil
49
- @request_in_progress = false
50
- end
51
-
52
- def response_buffer
53
- @response_buffer ||= ResponseBuffer.new(self, @response_processor)
54
- end
55
-
56
- def name
57
- if socket_type == :unix
58
- hostname
59
- else
60
- "#{hostname}:#{port}"
61
- end
32
+ @connection_manager = ConnectionManager.new(hostname, port, socket_type, @options)
33
+ @response_processor = ResponseProcessor.new(@connection_manager, @value_marshaller)
62
34
  end
63
35
 
64
36
  # Chokepoint method for error handling and ensuring liveness
65
37
  def request(opkey, *args)
66
38
  verify_state(opkey)
67
- # The alive? call has the side effect of connecting the underlying
68
- # socket if it is not connected, or there's been a disconnect
69
- # because of timeout or other error. Method raises an error
70
- # if it can't connect
71
- raise_memcached_down_err unless alive?
72
39
 
73
40
  begin
74
41
  send(opkey, *args)
75
42
  rescue Dalli::MarshalError => e
76
- log_marshall_err(args.first, e)
43
+ log_marshal_err(args.first, e)
77
44
  raise
78
- rescue Dalli::DalliError, Dalli::NetworkError, Dalli::ValueOverMaxSize, Timeout::Error
45
+ rescue Dalli::DalliError
79
46
  raise
80
47
  rescue StandardError => e
81
48
  log_unexpected_err(e)
@@ -83,63 +50,17 @@ module Dalli
83
50
  end
84
51
  end
85
52
 
86
- def raise_memcached_down_err
87
- raise Dalli::NetworkError,
88
- "#{name} is down: #{@error} #{@msg}. If you are sure it is running, "\
89
- "ensure memcached version is > #{::Dalli::MIN_SUPPORTED_MEMCACHED_VERSION}."
90
- end
91
-
92
- def log_marshall_err(key, err)
93
- Dalli.logger.error "Marshalling error for key '#{key}': #{err.message}"
94
- Dalli.logger.error 'You are trying to cache a Ruby object which cannot be serialized to memcached.'
95
- end
96
-
97
- def log_unexpected_err(err)
98
- Dalli.logger.error "Unexpected exception during Dalli request: #{err.class.name}: #{err.message}"
99
- Dalli.logger.error err.backtrace.join("\n\t")
100
- end
101
-
102
- # The socket connection to the underlying server is initialized as a side
103
- # effect of this call. In fact, this is the ONLY place where that
104
- # socket connection is initialized.
53
+ ##
54
+ # Boolean method used by clients of this class to determine if this
55
+ # particular memcached instance is available for use.
105
56
  def alive?
106
- return true if @sock
107
- return false unless reconnect_down_server?
108
-
109
- connect
110
- !!@sock
57
+ ensure_connected!
111
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.
112
61
  false
113
62
  end
114
63
 
115
- def reconnect_down_server?
116
- return true unless @last_down_at
117
-
118
- time_to_next_reconnect = @last_down_at + options[:down_retry_delay] - Time.now
119
- return true unless time_to_next_reconnect.positive?
120
-
121
- Dalli.logger.debug do
122
- format('down_retry_delay not reached for %<name>s (%<time>.3f seconds left)', name: name,
123
- time: time_to_next_reconnect)
124
- end
125
- false
126
- end
127
-
128
- # Closes the underlying socket and cleans up
129
- # socket state.
130
- def close
131
- return unless @sock
132
-
133
- begin
134
- @sock.close
135
- rescue StandardError
136
- nil
137
- end
138
- @sock = nil
139
- @pid = nil
140
- abort_request!
141
- end
142
-
143
64
  def lock!; end
144
65
 
145
66
  def unlock!; end
@@ -149,224 +70,125 @@ module Dalli
149
70
  # flushing responses for kv pairs that were found.
150
71
  #
151
72
  # Returns nothing.
152
- def pipeline_response_start
73
+ def pipeline_response_setup
153
74
  verify_state(:getkq)
154
75
  write_noop
155
76
  response_buffer.reset
156
- start_request!
157
- end
158
-
159
- # Did the last call to #pipeline_response_start complete successfully?
160
- def pipeline_response_completed?
161
- response_buffer.completed?
162
- end
163
-
164
- def pipeline_response(bytes_to_advance = 0)
165
- response_buffer.process_single_response(bytes_to_advance)
166
- end
167
-
168
- def reconnect_on_pipeline_complete!
169
- reconnect! 'multi_response has completed' if pipeline_response_completed?
77
+ @connection_manager.start_request!
170
78
  end
171
79
 
172
80
  # Attempt to receive and parse as many key/value pairs as possible
173
- # from this server. After #pipeline_response_start, this should be invoked
81
+ # from this server. After #pipeline_response_setup, this should be invoked
174
82
  # repeatedly whenever this server's socket is readable until
175
- # #pipeline_response_completed?.
83
+ # #pipeline_complete?.
176
84
  #
177
85
  # Returns a Hash of kv pairs received.
178
- def process_outstanding_pipeline_requests
86
+ def pipeline_next_responses
179
87
  reconnect_on_pipeline_complete!
180
88
  values = {}
181
89
 
182
90
  response_buffer.read
183
91
 
184
- bytes_to_advance, status, key, value, cas = pipeline_response
185
- # Loop while we have at least a complete header in the buffer
186
- while bytes_to_advance.positive?
187
- # If the status and key length are both zero, then this is the response
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
188
97
  # to the noop at the end of the pipeline
189
- if status.zero? && key.nil?
190
- finish_pipeline
191
- break
192
- end
98
+ finish_pipeline && break if resp_header.ok? && key.nil?
193
99
 
194
- # If the status is zero and the key len is positive, then this is a
100
+ # If the status is ok and the key is not nil, then this is a
195
101
  # getkq response with a value that we want to set in the response hash
196
- values[key] = [value, cas] unless key.nil?
102
+ values[key] = [value, resp_header.cas] unless key.nil?
197
103
 
198
- # Get the next set of bytes from the buffer
199
- bytes_to_advance, status, key, value, cas = pipeline_response(bytes_to_advance)
104
+ # Get the next response from the buffer
105
+ resp_header, key, value = pipeline_response
200
106
  end
201
107
 
202
108
  values
203
109
  rescue SystemCallError, Timeout::Error, EOFError => e
204
- failure!(e)
205
- end
206
-
207
- def read_nonblock
208
- @sock.read_available
209
- end
210
-
211
- # Called after the noop response is received at the end of a set
212
- # of pipelined gets
213
- def finish_pipeline
214
- response_buffer.clear
215
- finish_request!
110
+ @connection_manager.error_on_request!(e)
216
111
  end
217
112
 
218
- # Abort an earlier #pipeline_response_start. Used to signal an external
219
- # timeout. The underlying socket is disconnected, and the exception is
220
- # swallowed.
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.
221
116
  #
222
117
  # Returns nothing.
223
- def pipeline_response_abort
118
+ def pipeline_abort
224
119
  response_buffer.clear
225
- abort_request!
226
- return true unless @sock
120
+ @connection_manager.abort_request!
121
+ return true unless connected?
227
122
 
228
- failure!(RuntimeError.new('External timeout'))
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')
229
126
  rescue NetworkError
230
127
  true
231
128
  end
232
129
 
233
- def read(count)
234
- start_request!
235
- data = @sock.readfull(count)
236
- finish_request!
237
- data
238
- rescue SystemCallError, Timeout::Error, EOFError => e
239
- failure!(e)
130
+ # Did the last call to #pipeline_response_setup complete successfully?
131
+ def pipeline_complete?
132
+ !response_buffer.in_progress?
240
133
  end
241
134
 
242
- def write(bytes)
243
- start_request!
244
- result = @sock.write(bytes)
245
- finish_request!
246
- result
247
- rescue SystemCallError, Timeout::Error => e
248
- failure!(e)
135
+ def username
136
+ @options[:username] || ENV['MEMCACHE_USERNAME']
249
137
  end
250
138
 
251
- def connected?
252
- !@sock.nil?
139
+ def password
140
+ @options[:password] || ENV['MEMCACHE_PASSWORD']
253
141
  end
254
142
 
255
- def socket_timeout
256
- @socket_timeout ||= @options[:socket_timeout]
143
+ def require_auth?
144
+ !username.nil?
257
145
  end
258
146
 
259
147
  # NOTE: Additional public methods should be overridden in Dalli::Threadsafe
260
148
 
261
149
  private
262
150
 
263
- def request_in_progress?
264
- @request_in_progress
265
- end
266
-
267
- def start_request!
268
- @request_in_progress = true
269
- end
270
-
271
- def finish_request!
272
- @request_in_progress = false
273
- end
274
-
275
- def abort_request!
276
- @request_in_progress = false
277
- end
278
-
151
+ ##
152
+ # Checks to see if we can execute the specified operation. Checks
153
+ # whether the connection is in use, and whether the command is allowed
154
+ ##
279
155
  def verify_state(opkey)
280
- failure!(RuntimeError.new('Already writing to socket')) if request_in_progress?
281
- reconnect_on_fork if fork_detected?
282
- verify_allowed_multi!(opkey) if multi?
283
- end
284
-
285
- def fork_detected?
286
- @pid && @pid != Process.pid
287
- end
288
-
289
- ALLOWED_MULTI_OPS = %i[add addq delete deleteq replace replaceq set setq noop].freeze
290
- def verify_allowed_multi!(opkey)
291
- return if ALLOWED_MULTI_OPS.include?(opkey)
292
-
293
- raise Dalli::NotPermittedMultiOpError, "The operation #{opkey} is not allowed in a multi block."
294
- end
295
-
296
- def reconnect_on_fork
297
- message = 'Fork detected, re-connecting child process...'
298
- Dalli.logger.info { message }
299
- reconnect! message
300
- end
301
-
302
- # Marks the server instance as needing reconnect. Raises a
303
- # Dalli::NetworkError with the specified message. Calls close
304
- # to clean up socket state
305
- def reconnect!(message)
306
- close
307
- sleep(options[:socket_failure_delay]) if options[:socket_failure_delay]
308
- raise Dalli::NetworkError, message
309
- end
310
-
311
- # Raises Dalli::NetworkError
312
- def failure!(exception)
313
- message = "#{name} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
314
- Dalli.logger.warn { message }
315
-
316
- @fail_count += 1
317
- if @fail_count >= options[:socket_max_failures]
318
- down!
319
- else
320
- reconnect! 'Socket operation failed, retrying...'
321
- end
322
- end
323
-
324
- # Marks the server instance as down. Updates the down_at state
325
- # and raises an Dalli::NetworkError that includes the underlying
326
- # error in the message. Calls close to clean up socket state
327
- def down!
328
- close
329
- log_down_detected
156
+ @connection_manager.confirm_ready!
157
+ verify_allowed_quiet!(opkey) if quiet?
330
158
 
331
- @error = $ERROR_INFO&.class&.name
332
- @msg ||= $ERROR_INFO&.message
333
- raise Dalli::NetworkError, "#{name} is down: #{@error} #{@msg}"
334
- end
335
-
336
- def log_down_detected
337
- @last_down_at = Time.now
338
-
339
- if @down_at
340
- time = Time.now - @down_at
341
- Dalli.logger.debug { format('%<name>s is still down (for %<time>.3f seconds now)', name: name, time: time) }
342
- else
343
- @down_at = @last_down_at
344
- Dalli.logger.warn("#{name} is down")
345
- end
159
+ # The ensure_connected call has the side effect of connecting the
160
+ # underlying socket if it is not connected, or there's been a disconnect
161
+ # because of timeout or other error. Method raises an error
162
+ # if it can't connect
163
+ raise_down_error unless ensure_connected!
346
164
  end
347
165
 
348
- def log_up_detected
349
- return unless @down_at
166
+ # The socket connection to the underlying server is initialized as a side
167
+ # effect of this call. In fact, this is the ONLY place where that
168
+ # socket connection is initialized.
169
+ #
170
+ # Both this method and connect need to be in this class so we can do auth
171
+ # as required
172
+ #
173
+ # Since this is invoked exclusively in verify_state!, we don't need to worry about
174
+ # thread safety. Using it elsewhere may require revisiting that assumption.
175
+ def ensure_connected!
176
+ return true if connected?
177
+ return false unless reconnect_down_server?
350
178
 
351
- time = Time.now - @down_at
352
- Dalli.logger.warn { format('%<name>s is back (downtime was %<time>.3f seconds)', name: name, time: time) }
179
+ connect # This call needs to be in this class so we can do auth
180
+ connected?
353
181
  end
354
182
 
355
- def up!
356
- log_up_detected
357
- reset_down_info
358
- end
183
+ ALLOWED_QUIET_OPS = %i[add replace set delete incr decr append prepend flush noop].freeze
184
+ def verify_allowed_quiet!(opkey)
185
+ return if ALLOWED_QUIET_OPS.include?(opkey)
359
186
 
360
- def reset_down_info
361
- @fail_count = 0
362
- @down_at = nil
363
- @last_down_at = nil
364
- @msg = nil
365
- @error = nil
187
+ raise Dalli::NotPermittedMultiOpError, "The operation #{opkey} is not allowed in a quiet block."
366
188
  end
367
189
 
368
- def multi?
369
- Thread.current[::Dalli::MULTI_KEY]
190
+ def quiet?
191
+ Thread.current[::Dalli::QUIET]
370
192
  end
371
193
 
372
194
  def cache_nils?(opts)
@@ -375,39 +197,52 @@ module Dalli
375
197
  opts[:cache_nils] ? true : false
376
198
  end
377
199
 
200
+ # Retrieval Commands
378
201
  def get(key, options = nil)
379
202
  req = RequestFormatter.standard_request(opkey: :get, key: key)
380
203
  write(req)
381
204
  @response_processor.generic_response(unpack: true, cache_nils: cache_nils?(options))
382
205
  end
383
206
 
384
- def pipelined_get(keys)
385
- req = +''
386
- keys.each do |key|
387
- req << RequestFormatter.standard_request(opkey: :getkq, key: key)
388
- end
389
- # Could send noop here instead of in pipeline_response_start
207
+ def gat(key, ttl, options = nil)
208
+ ttl = TtlSanitizer.sanitize(ttl)
209
+ req = RequestFormatter.standard_request(opkey: :gat, key: key, ttl: ttl)
390
210
  write(req)
211
+ @response_processor.generic_response(unpack: true, cache_nils: cache_nils?(options))
391
212
  end
392
213
 
214
+ def touch(key, ttl)
215
+ ttl = TtlSanitizer.sanitize(ttl)
216
+ write(RequestFormatter.standard_request(opkey: :touch, key: key, ttl: ttl))
217
+ @response_processor.generic_response
218
+ end
219
+
220
+ # TODO: This is confusing, as there's a cas command in memcached
221
+ # and this isn't it. Maybe rename? Maybe eliminate?
222
+ def cas(key)
223
+ req = RequestFormatter.standard_request(opkey: :get, key: key)
224
+ write(req)
225
+ @response_processor.data_cas_response
226
+ end
227
+
228
+ # Storage Commands
393
229
  def set(key, value, ttl, cas, options)
394
- opkey = multi? ? :setq : :set
395
- process_value_req(opkey, key, value, ttl, cas, options)
230
+ opkey = quiet? ? :setq : :set
231
+ storage_req(opkey, key, value, ttl, cas, options)
396
232
  end
397
233
 
398
234
  def add(key, value, ttl, options)
399
- opkey = multi? ? :addq : :add
400
- cas = 0
401
- process_value_req(opkey, key, value, ttl, cas, options)
235
+ opkey = quiet? ? :addq : :add
236
+ storage_req(opkey, key, value, ttl, 0, options)
402
237
  end
403
238
 
404
239
  def replace(key, value, ttl, cas, options)
405
- opkey = multi? ? :replaceq : :replace
406
- process_value_req(opkey, key, value, ttl, cas, options)
240
+ opkey = quiet? ? :replaceq : :replace
241
+ storage_req(opkey, key, value, ttl, cas, options)
407
242
  end
408
243
 
409
244
  # rubocop:disable Metrics/ParameterLists
410
- def process_value_req(opkey, key, value, ttl, cas, options)
245
+ def storage_req(opkey, key, value, ttl, cas, options)
411
246
  (value, bitflags) = @value_marshaller.store(key, value, options)
412
247
  ttl = TtlSanitizer.sanitize(ttl)
413
248
 
@@ -415,21 +250,42 @@ module Dalli
415
250
  value: value, bitflags: bitflags,
416
251
  ttl: ttl, cas: cas)
417
252
  write(req)
418
- @response_processor.cas_response unless multi?
253
+ @response_processor.storage_response unless quiet?
419
254
  end
420
255
  # rubocop:enable Metrics/ParameterLists
421
256
 
257
+ def append(key, value)
258
+ opkey = quiet? ? :appendq : :append
259
+ write_append_prepend opkey, key, value
260
+ end
261
+
262
+ def prepend(key, value)
263
+ opkey = quiet? ? :prependq : :prepend
264
+ write_append_prepend opkey, key, value
265
+ end
266
+
267
+ def write_append_prepend(opkey, key, value)
268
+ write(RequestFormatter.standard_request(opkey: opkey, key: key, value: value))
269
+ @response_processor.no_body_response unless quiet?
270
+ end
271
+
272
+ # Delete Commands
422
273
  def delete(key, cas)
423
- opkey = multi? ? :deleteq : :delete
274
+ opkey = quiet? ? :deleteq : :delete
424
275
  req = RequestFormatter.standard_request(opkey: opkey, key: key, cas: cas)
425
276
  write(req)
426
- @response_processor.generic_response unless multi?
277
+ @response_processor.no_body_response unless quiet?
427
278
  end
428
279
 
429
- def flush(ttl = 0)
430
- req = RequestFormatter.standard_request(opkey: :flush, ttl: ttl)
431
- write(req)
432
- @response_processor.generic_response
280
+ # Arithmetic Commands
281
+ def decr(key, count, ttl, initial)
282
+ opkey = quiet? ? :decrq : :decr
283
+ decr_incr opkey, key, count, ttl, initial
284
+ end
285
+
286
+ def incr(key, count, ttl, initial)
287
+ opkey = quiet? ? :incrq : :incr
288
+ decr_incr opkey, key, count, ttl, initial
433
289
  end
434
290
 
435
291
  # This allows us to special case a nil initial value, and
@@ -444,29 +300,14 @@ module Dalli
444
300
  initial ||= 0
445
301
  write(RequestFormatter.decr_incr_request(opkey: opkey, key: key,
446
302
  count: count, initial: initial, expiry: expiry))
447
- @response_processor.decr_incr_response
303
+ @response_processor.decr_incr_response unless quiet?
448
304
  end
449
305
 
450
- def decr(key, count, ttl, initial)
451
- decr_incr :decr, key, count, ttl, initial
452
- end
453
-
454
- def incr(key, count, ttl, initial)
455
- decr_incr :incr, key, count, ttl, initial
456
- end
457
-
458
- def write_append_prepend(opkey, key, value)
459
- write_generic RequestFormatter.standard_request(opkey: opkey, key: key, value: value)
460
- end
461
-
462
- def write_generic(bytes)
463
- write(bytes)
464
- @response_processor.generic_response
465
- end
466
-
467
- def write_noop
468
- req = RequestFormatter.standard_request(opkey: :noop)
469
- write(req)
306
+ # Other Commands
307
+ def flush(ttl = 0)
308
+ opkey = quiet? ? :flushq : :flush
309
+ write(RequestFormatter.standard_request(opkey: opkey, ttl: ttl))
310
+ @response_processor.no_body_response unless quiet?
470
311
  end
471
312
 
472
313
  # Noop is a keepalive operation but also used to demarcate the end of a set of pipelined commands.
@@ -476,14 +317,6 @@ module Dalli
476
317
  @response_processor.multi_with_keys_response
477
318
  end
478
319
 
479
- def append(key, value)
480
- write_append_prepend :append, key, value
481
- end
482
-
483
- def prepend(key, value)
484
- write_append_prepend :prepend, key, value
485
- end
486
-
487
320
  def stats(info = '')
488
321
  req = RequestFormatter.standard_request(opkey: :stat, key: info)
489
322
  write(req)
@@ -491,66 +324,67 @@ module Dalli
491
324
  end
492
325
 
493
326
  def reset_stats
494
- write_generic RequestFormatter.standard_request(opkey: :stat, key: 'reset')
327
+ write(RequestFormatter.standard_request(opkey: :stat, key: 'reset'))
328
+ @response_processor.generic_response
495
329
  end
496
330
 
497
- def cas(key)
498
- req = RequestFormatter.standard_request(opkey: :get, key: key)
499
- write(req)
500
- @response_processor.data_cas_response
331
+ def version
332
+ write(RequestFormatter.standard_request(opkey: :version))
333
+ @response_processor.generic_response
501
334
  end
502
335
 
503
- def version
504
- write_generic RequestFormatter.standard_request(opkey: :version)
336
+ def write_noop
337
+ req = RequestFormatter.standard_request(opkey: :noop)
338
+ write(req)
505
339
  end
506
340
 
507
- def touch(key, ttl)
508
- ttl = TtlSanitizer.sanitize(ttl)
509
- write_generic RequestFormatter.standard_request(opkey: :touch, key: key, ttl: ttl)
341
+ def connect
342
+ @connection_manager.establish_connection
343
+ authenticate_connection if require_auth?
344
+ @version = version # Connect socket if not authed
345
+ up!
346
+ rescue Dalli::DalliError
347
+ raise
510
348
  end
511
349
 
512
- def gat(key, ttl, options = nil)
513
- ttl = TtlSanitizer.sanitize(ttl)
514
- req = RequestFormatter.standard_request(opkey: :gat, key: key, ttl: ttl)
350
+ def pipelined_get(keys)
351
+ req = +''
352
+ keys.each do |key|
353
+ req << RequestFormatter.standard_request(opkey: :getkq, key: key)
354
+ end
355
+ # Could send noop here instead of in pipeline_response_setup
515
356
  write(req)
516
- @response_processor.generic_response(unpack: true, cache_nils: cache_nils?(options))
517
357
  end
518
358
 
519
- def connect
520
- Dalli.logger.debug { "Dalli::Server#connect #{name}" }
359
+ def response_buffer
360
+ @response_buffer ||= ResponseBuffer.new(@connection_manager, @response_processor)
361
+ end
521
362
 
522
- begin
523
- @pid = Process.pid
524
- @sock = memcached_socket
525
- authenticate_connection if require_auth?
526
- @version = version # Connect socket if not authed
527
- up!
528
- rescue Dalli::DalliError # SASL auth failure
529
- raise
530
- rescue SystemCallError, Timeout::Error, EOFError, SocketError => e
531
- # SocketError = DNS resolution failure
532
- failure!(e)
533
- end
363
+ def pipeline_response
364
+ response_buffer.process_single_getk_response
534
365
  end
535
366
 
536
- def memcached_socket
537
- if socket_type == :unix
538
- Dalli::Socket::UNIX.open(hostname, options)
539
- else
540
- Dalli::Socket::TCP.open(hostname, port, options)
541
- end
367
+ # Called after the noop response is received at the end of a set
368
+ # of pipelined gets
369
+ def finish_pipeline
370
+ response_buffer.clear
371
+ @connection_manager.finish_request!
372
+
373
+ true # to simplify response
542
374
  end
543
375
 
544
- def require_auth?
545
- !username.nil?
376
+ def reconnect_on_pipeline_complete!
377
+ @connection_manager.reconnect! 'pipelined get has completed' if pipeline_complete?
546
378
  end
547
379
 
548
- def username
549
- @options[:username] || ENV['MEMCACHE_USERNAME']
380
+ def log_marshal_err(key, err)
381
+ Dalli.logger.error "Marshalling error for key '#{key}': #{err.message}"
382
+ Dalli.logger.error 'You are trying to cache a Ruby object which cannot be serialized to memcached.'
550
383
  end
551
384
 
552
- def password
553
- @options[:password] || ENV['MEMCACHE_PASSWORD']
385
+ def log_unexpected_err(err)
386
+ Dalli.logger.error "Unexpected exception during Dalli request: #{err.class.name}: #{err.message}"
387
+ Dalli.logger.error err.backtrace.join("\n\t")
554
388
  end
555
389
 
556
390
  include SaslAuthentication