Class: Dalli::Protocol::ConnectionManager
- Inherits:
-
Object
- Object
- Dalli::Protocol::ConnectionManager
- Defined in:
- lib/dalli/protocol/connection_manager.rb
Overview
Manages the socket connection to the server, including ensuring liveness and retries.
Constant Summary collapse
- DEFAULTS =
{ # seconds between trying to contact a remote server down_retry_delay: 30, # connect/read/write timeout for socket operations socket_timeout: 1, # times a socket operation may fail before considering the server dead socket_max_failures: 2, # amount of time to sleep between retries when a failure occurs socket_failure_delay: 0.1, # Set keepalive keepalive: true }.freeze
Instance Attribute Summary collapse
-
#hostname ⇒ Object
Returns the value of attribute hostname.
-
#options ⇒ Object
Returns the value of attribute options.
-
#port ⇒ Object
Returns the value of attribute port.
-
#sock ⇒ Object
readonly
Returns the value of attribute sock.
-
#socket_type ⇒ Object
Returns the value of attribute socket_type.
Instance Method Summary collapse
- #abort_request! ⇒ Object
- #close ⇒ Object
- #confirm_in_progress! ⇒ Object
- #confirm_ready! ⇒ Object
- #connected? ⇒ Boolean
-
#down! ⇒ Object
Marks the server instance as down.
- #error_on_request!(err_or_string) ⇒ Object
- #establish_connection ⇒ Object
- #finish_request! ⇒ Object
- #flush ⇒ Object
- #fork_detected? ⇒ Boolean
-
#initialize(hostname, port, socket_type, client_options) ⇒ ConnectionManager
constructor
A new instance of ConnectionManager.
- #log_down_detected ⇒ Object
- #log_up_detected ⇒ Object
- #log_warn_message(err_or_string) ⇒ Object
- #max_allowed_failures ⇒ Object
- #memcached_socket ⇒ Object
- #name ⇒ Object
- #raise_down_error ⇒ Object
- #read(count) ⇒ Object
- #read_line ⇒ Object
-
#read_nonblock ⇒ Object
Non-blocking read.
- #reconnect!(message) ⇒ Object
- #reconnect_down_server? ⇒ Boolean
- #reconnect_on_fork ⇒ Object
- #request_in_progress? ⇒ Boolean
- #reset_down_info ⇒ Object
- #socket_timeout ⇒ Object
- #start_request! ⇒ Object
- #up! ⇒ Object
- #write(bytes) ⇒ Object
Constructor Details
#initialize(hostname, port, socket_type, client_options) ⇒ ConnectionManager
Returns a new instance of ConnectionManager.
32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/dalli/protocol/connection_manager.rb', line 32 def initialize(hostname, port, socket_type, ) @hostname = hostname @port = port @socket_type = socket_type @options = DEFAULTS.merge() @request_in_progress = false @sock = nil @pid = nil reset_down_info end |
Instance Attribute Details
#hostname ⇒ Object
Returns the value of attribute hostname.
29 30 31 |
# File 'lib/dalli/protocol/connection_manager.rb', line 29 def hostname @hostname end |
#options ⇒ Object
Returns the value of attribute options.
29 30 31 |
# File 'lib/dalli/protocol/connection_manager.rb', line 29 def @options end |
#port ⇒ Object
Returns the value of attribute port.
29 30 31 |
# File 'lib/dalli/protocol/connection_manager.rb', line 29 def port @port end |
#sock ⇒ Object (readonly)
Returns the value of attribute sock.
30 31 32 |
# File 'lib/dalli/protocol/connection_manager.rb', line 30 def sock @sock end |
#socket_type ⇒ Object
Returns the value of attribute socket_type.
29 30 31 |
# File 'lib/dalli/protocol/connection_manager.rb', line 29 def socket_type @socket_type end |
Instance Method Details
#abort_request! ⇒ Object
146 147 148 |
# File 'lib/dalli/protocol/connection_manager.rb', line 146 def abort_request! @request_in_progress = false end |
#close ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/dalli/protocol/connection_manager.rb', line 113 def close return unless @sock begin @sock.close rescue StandardError nil end @sock = nil @pid = nil abort_request! end |
#confirm_in_progress! ⇒ Object
107 108 109 110 111 |
# File 'lib/dalli/protocol/connection_manager.rb', line 107 def confirm_in_progress! raise '[Dalli] No request in progress. This may be a bug in Dalli.' unless request_in_progress? reconnect_on_fork if fork_detected? end |
#confirm_ready! ⇒ Object
102 103 104 105 |
# File 'lib/dalli/protocol/connection_manager.rb', line 102 def confirm_ready! close if request_in_progress? reconnect_on_fork if fork_detected? end |
#connected? ⇒ Boolean
126 127 128 |
# File 'lib/dalli/protocol/connection_manager.rb', line 126 def connected? !@sock.nil? end |
#down! ⇒ Object
Marks the server instance as down. Updates the down_at state and raises an Dalli::NetworkError that includes the underlying error in the message. Calls close to clean up socket state
85 86 87 88 89 90 91 92 |
# File 'lib/dalli/protocol/connection_manager.rb', line 85 def down! close log_down_detected @error = $ERROR_INFO&.class&.name @msg ||= $ERROR_INFO&. raise_down_error end |
#error_on_request!(err_or_string) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/dalli/protocol/connection_manager.rb', line 192 def error_on_request!(err_or_string) (err_or_string) @fail_count += 1 if @fail_count >= max_allowed_failures down! else # Closes the existing socket, setting up for a reconnect # on next request reconnect!('Socket operation failed, retrying...') end end |
#establish_connection ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/dalli/protocol/connection_manager.rb', line 52 def establish_connection Dalli.logger.debug { "Dalli::Server#connect #{name}" } @sock = memcached_socket @sock.sync = false # Enable buffered I/O for better performance @pid = PIDCache.pid @request_in_progress = false rescue SystemCallError, *TIMEOUT_ERRORS, EOFError, SocketError => e # SocketError = DNS resolution failure error_on_request!(e) end |
#finish_request! ⇒ Object
140 141 142 143 144 |
# File 'lib/dalli/protocol/connection_manager.rb', line 140 def finish_request! raise '[Dalli] No request in progress. This may be a bug in Dalli.' unless @request_in_progress @request_in_progress = false end |
#flush ⇒ Object
176 177 178 179 180 |
# File 'lib/dalli/protocol/connection_manager.rb', line 176 def flush @sock.flush rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, IOError => e error_on_request!(e) end |
#fork_detected? ⇒ Boolean
242 243 244 |
# File 'lib/dalli/protocol/connection_manager.rb', line 242 def fork_detected? @pid && @pid != PIDCache.pid end |
#log_down_detected ⇒ Object
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/dalli/protocol/connection_manager.rb', line 246 def log_down_detected @last_down_at = Time.now if @down_at time = Time.now - @down_at Dalli.logger.debug { format('%<name>s is still down (for %<time>.3f seconds now)', name: name, time: time) } else @down_at = @last_down_at Dalli.logger.warn("#{name} is down") end end |
#log_up_detected ⇒ Object
258 259 260 261 262 263 |
# File 'lib/dalli/protocol/connection_manager.rb', line 258 def log_up_detected return unless @down_at time = Time.now - @down_at Dalli.logger.warn { format('%<name>s is back (downtime was %<time>.3f seconds)', name: name, time: time) } end |
#log_warn_message(err_or_string) ⇒ Object
227 228 229 230 231 232 |
# File 'lib/dalli/protocol/connection_manager.rb', line 227 def (err_or_string) Dalli.logger.warn do detail = err_or_string.is_a?(String) ? err_or_string : "#{err_or_string.class}: #{err_or_string.}" "#{name} failed (count: #{@fail_count}) #{detail}" end end |
#max_allowed_failures ⇒ Object
188 189 190 |
# File 'lib/dalli/protocol/connection_manager.rb', line 188 def max_allowed_failures @max_allowed_failures ||= @options[:socket_max_failures] || 2 end |
#memcached_socket ⇒ Object
219 220 221 222 223 224 225 |
# File 'lib/dalli/protocol/connection_manager.rb', line 219 def memcached_socket if socket_type == :unix Dalli::Socket::UNIX.open(hostname, ) else Dalli::Socket::TCP.open(hostname, port, ) end end |
#name ⇒ Object
44 45 46 47 48 49 50 |
# File 'lib/dalli/protocol/connection_manager.rb', line 44 def name if socket_type == :unix hostname else "#{hostname}:#{port}" end end |
#raise_down_error ⇒ Object
94 95 96 |
# File 'lib/dalli/protocol/connection_manager.rb', line 94 def raise_down_error raise Dalli::NetworkError, "#{name} is down: #{@error} #{@msg}" end |
#read(count) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/dalli/protocol/connection_manager.rb', line 158 def read(count) # JRuby doesn't support IO#timeout=, so use custom readfull implementation # CRuby 3.3+ has IO#timeout= which makes IO#read work with timeouts if RUBY_ENGINE == 'jruby' @sock.readfull(count) else @sock.read(count) end rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, EOFError => e error_on_request!(e) end |
#read_line ⇒ Object
150 151 152 153 154 155 156 |
# File 'lib/dalli/protocol/connection_manager.rb', line 150 def read_line data = @sock.gets("\r\n") error_on_request!('EOF in read_line') if data.nil? data rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, EOFError => e error_on_request!(e) end |
#read_nonblock ⇒ Object
Non-blocking read. Here to support the operation of the get_multi operation
184 185 186 |
# File 'lib/dalli/protocol/connection_manager.rb', line 184 def read_nonblock @sock.read_available end |
#reconnect!(message) ⇒ Object
205 206 207 208 209 |
# File 'lib/dalli/protocol/connection_manager.rb', line 205 def reconnect!() close sleep([:socket_failure_delay]) if [:socket_failure_delay] raise Dalli::RetryableNetworkError, end |
#reconnect_down_server? ⇒ Boolean
64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/dalli/protocol/connection_manager.rb', line 64 def reconnect_down_server? return true unless @last_down_at time_to_next_reconnect = @last_down_at + [:down_retry_delay] - Time.now return true unless time_to_next_reconnect.positive? Dalli.logger.debug do format('down_retry_delay not reached for %<name>s (%<time>.3f seconds left)', name: name, time: time_to_next_reconnect) end false end |
#reconnect_on_fork ⇒ Object
234 235 236 237 238 239 240 |
# File 'lib/dalli/protocol/connection_manager.rb', line 234 def reconnect_on_fork = 'Fork detected, re-connecting child process...' Dalli.logger.info { } # Close socket on a fork and reconnect immediately close establish_connection end |
#request_in_progress? ⇒ Boolean
130 131 132 |
# File 'lib/dalli/protocol/connection_manager.rb', line 130 def request_in_progress? @request_in_progress end |
#reset_down_info ⇒ Object
211 212 213 214 215 216 217 |
# File 'lib/dalli/protocol/connection_manager.rb', line 211 def reset_down_info @fail_count = 0 @down_at = nil @last_down_at = nil @msg = nil @error = nil end |
#socket_timeout ⇒ Object
98 99 100 |
# File 'lib/dalli/protocol/connection_manager.rb', line 98 def socket_timeout @socket_timeout ||= @options[:socket_timeout] end |
#start_request! ⇒ Object
134 135 136 137 138 |
# File 'lib/dalli/protocol/connection_manager.rb', line 134 def start_request! raise '[Dalli] Request already in progress. This may be a bug in Dalli.' if @request_in_progress @request_in_progress = true end |
#up! ⇒ Object
77 78 79 80 |
# File 'lib/dalli/protocol/connection_manager.rb', line 77 def up! log_up_detected reset_down_info end |
#write(bytes) ⇒ Object
170 171 172 173 174 |
# File 'lib/dalli/protocol/connection_manager.rb', line 170 def write(bytes) @sock.write(bytes) rescue SystemCallError, *TIMEOUT_ERRORS, *SSL_ERRORS, IOError => e error_on_request!(e) end |