From 72413309da8c9dc4ae00a1602117fe34aee7cb17 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Thu, 28 May 2015 21:38:23 -0500 Subject: [PATCH 1/9] Initial attempt at getting keepalive to work --- lib/moped/connection.rb | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 08aa576..5a564ae 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -37,6 +37,53 @@ def alive? connected? ? @sock.alive? : false end + + + if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c} + def set_tcp_keepalive(keepalive,sock) + case keepalive + when Hash + [:time, :intvl, :probes].each do |key| + unless keepalive[key].is_a?(Fixnum) + raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum" + end + end + when Fixnum + if keepalive >= 60 + keepalive = {:time => keepalive - 20, :intvl => 10, :probes => 2} + + elsif keepalive >= 30 + keepalive = {:time => keepalive - 10, :intvl => 5, :probes => 2} + + elsif keepalive >= 5 + keepalive = {:time => keepalive - 2, :intvl => 2, :probes => 1} + end + end + + sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) + sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, keepalive[:time]) + sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, keepalive[:intvl]) + sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, keepalive[:probes]) + Rails.logger.debug "Configured TCP Keepalive for Moped Connection to #{keepalive.inspect}" + end + + def get_tcp_keepalive + { + :time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int, + :intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int, + :probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int, + } + end + else + def set_tcp_keepalive(keepalive, sock) + end + + def get_tcp_keepalive + { + } + end + end + # Connect to the server defined by @host, @port without timeout @timeout. # # @example Open the connection @@ -52,6 +99,7 @@ def connect else Socket::TCP.connect(host, port, timeout) end + set_tcp_keepalive(60, @sock) end # Is the connection connected? From 1b3690b05225c7b8d88c120da1ee39d9c5bcf57d Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Thu, 28 May 2015 22:05:57 -0500 Subject: [PATCH 2/9] TCP Keepalive support on systems that support it (i.e. Linux) --- lib/moped/connection.rb | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 5a564ae..53cef39 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require "moped/connection/manager" require "moped/connection/sockets" +require "socket" module Moped @@ -39,7 +40,7 @@ def alive? - if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c} + if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c} def set_tcp_keepalive(keepalive,sock) case keepalive when Hash @@ -60,22 +61,23 @@ def set_tcp_keepalive(keepalive,sock) end end - sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) - sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, keepalive[:time]) - sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, keepalive[:intvl]) - sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, keepalive[:probes]) - Rails.logger.debug "Configured TCP Keepalive for Moped Connection to #{keepalive.inspect}" + sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) + sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive[:time]) + sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive[:intvl]) + sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive[:probes]) + puts "Configured TCP Keepalive for Moped Connection to #{keepalive.inspect}" end def get_tcp_keepalive { - :time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int, - :intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int, - :probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int, + :time => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE).int, + :intvl => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL).int, + :probes => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT).int, } end else def set_tcp_keepalive(keepalive, sock) + Rails.logger.debug "Did not configure TCP Keepalive for Moped Connection." end def get_tcp_keepalive @@ -99,7 +101,7 @@ def connect else Socket::TCP.connect(host, port, timeout) end - set_tcp_keepalive(60, @sock) + set_tcp_keepalive(5, @sock) end # Is the connection connected? From 5921af7fe4c8ac2c88b10b4a832e898690aea812 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Thu, 28 May 2015 23:09:16 -0500 Subject: [PATCH 3/9] Making the keepalive configurable --- lib/moped/connection.rb | 109 ++++++++++++++++++++++------------------ lib/moped/session.rb | 5 ++ 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 53cef39..856fc6a 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -38,53 +38,7 @@ def alive? connected? ? @sock.alive? : false end - - - if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c} - def set_tcp_keepalive(keepalive,sock) - case keepalive - when Hash - [:time, :intvl, :probes].each do |key| - unless keepalive[key].is_a?(Fixnum) - raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum" - end - end - when Fixnum - if keepalive >= 60 - keepalive = {:time => keepalive - 20, :intvl => 10, :probes => 2} - - elsif keepalive >= 30 - keepalive = {:time => keepalive - 10, :intvl => 5, :probes => 2} - - elsif keepalive >= 5 - keepalive = {:time => keepalive - 2, :intvl => 2, :probes => 1} - end - end - - sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) - sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive[:time]) - sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive[:intvl]) - sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive[:probes]) - puts "Configured TCP Keepalive for Moped Connection to #{keepalive.inspect}" - end - - def get_tcp_keepalive - { - :time => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE).int, - :intvl => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL).int, - :probes => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT).int, - } - end - else - def set_tcp_keepalive(keepalive, sock) - Rails.logger.debug "Did not configure TCP Keepalive for Moped Connection." - end - - def get_tcp_keepalive - { - } - end - end + # Connect to the server defined by @host, @port without timeout @timeout. # @@ -101,7 +55,7 @@ def connect else Socket::TCP.connect(host, port, timeout) end - set_tcp_keepalive(5, @sock) + set_tcp_keepalive(options[:keepalive], @sock) if !!options[:keepalive] end # Is the connection connected? @@ -253,6 +207,65 @@ def read_data(socket, length) data end + # Set the tcp_keepalive + # + # @api private + # + # @example With a FixNum. + # set_tcp_keepalive(60) + # + # @example With a Hash + # set_tcp_keepalive({time: 60, intvl: 20, probes: 2}) + # + # @return [ nil ] nil. + # + # @since 2.0.5 + if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c} + def set_tcp_keepalive(keepalive) + case keepalive + when Hash + [:time, :intvl, :probes].each do |key| + unless keepalive[key].is_a?(Fixnum) + raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum" + end + end + when Fixnum + if keepalive >= 60 + keepalive = {:time => keepalive - 20, :intvl => 10, :probes => 2} + + elsif keepalive >= 30 + keepalive = {:time => keepalive - 10, :intvl => 5, :probes => 2} + + elsif keepalive >= 5 + keepalive = {:time => keepalive - 2, :intvl => 2, :probes => 1} + end + end + + @sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) + @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive[:time]) + @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive[:intvl]) + @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive[:probes]) + puts "Configured TCP Keepalive for Moped Connection to #{keepalive.inspect}" + end + + def get_tcp_keepalive + { + :time => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE).int, + :intvl => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL).int, + :probes => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT).int, + } + end + else + def set_tcp_keepalive(keepalive, sock) + puts "Did not configure TCP Keepalive for Moped Connection. Keepalive: #{keepalive}" + end + + def get_tcp_keepalive + { + } + end + end + # Yields a connected socket to the calling back. It will attempt to reconnect # the socket if it is not connected. # diff --git a/lib/moped/session.rb b/lib/moped/session.rb index 54203fa..67f7eff 100644 --- a/lib/moped/session.rb +++ b/lib/moped/session.rb @@ -240,6 +240,11 @@ def logout # @since 2.0.0 option(:timeout).allow(Optionable.any(Numeric)) + # Setup validation of allowed timeout options. (Any numeric) + # + # @since 2.0.5 + option(:keepalive).allow(Optionable.any(Fixnum), Optionable.any(Hash)) + # Pass an object that responds to instrument as an instrumenter. # # @since 2.0.0 From 05792651a17d321fdd02396e5c984e2822d7d842 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Thu, 28 May 2015 23:21:47 -0500 Subject: [PATCH 4/9] Remove puts in favor of Loggable --- lib/moped/connection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 856fc6a..0fe29c4 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -245,7 +245,7 @@ def set_tcp_keepalive(keepalive) @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive[:time]) @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive[:intvl]) @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive[:probes]) - puts "Configured TCP Keepalive for Moped Connection to #{keepalive.inspect}" + Loggable.debug(" MOPED:", "Configured TCP keepalive for connection with #{keepalive.inspect}", "n/a") end def get_tcp_keepalive @@ -257,7 +257,7 @@ def get_tcp_keepalive end else def set_tcp_keepalive(keepalive, sock) - puts "Did not configure TCP Keepalive for Moped Connection. Keepalive: #{keepalive}" + Loggable.debug(" MOPED:", "Did not configure TCP keepalive for connection as it is not supported on this platform.", "n/a") end def get_tcp_keepalive From 78abcdac7a3e4d453ab9e04744c384821e574f94 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Thu, 28 May 2015 23:58:49 -0500 Subject: [PATCH 5/9] Fixing a stupid late night error where arguments where wrong --- lib/moped/connection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 0fe29c4..5d3357d 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -55,7 +55,7 @@ def connect else Socket::TCP.connect(host, port, timeout) end - set_tcp_keepalive(options[:keepalive], @sock) if !!options[:keepalive] + set_tcp_keepalive(options[:keepalive]) if !!options[:keepalive] end # Is the connection connected? @@ -256,7 +256,7 @@ def get_tcp_keepalive } end else - def set_tcp_keepalive(keepalive, sock) + def set_tcp_keepalive(keepalive) Loggable.debug(" MOPED:", "Did not configure TCP keepalive for connection as it is not supported on this platform.", "n/a") end From 09e3b65e9469ebec5170562679cc1f1dd0cbe530 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Fri, 29 May 2015 12:18:38 -0500 Subject: [PATCH 6/9] Cleaning up keepalive logic --- lib/moped/connection.rb | 121 +++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 5d3357d..b21f131 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -55,7 +55,7 @@ def connect else Socket::TCP.connect(host, port, timeout) end - set_tcp_keepalive(options[:keepalive]) if !!options[:keepalive] + set_tcp_keepalive end # Is the connection connected? @@ -104,6 +104,7 @@ def initialize(host, port, timeout, options = {}) @options = options @sock = nil @request_id = 0 + configure_tcp_keepalive(@options[:keepalive]) end # Read from the connection. @@ -180,6 +181,45 @@ def write(operations) private + + # Configure the TCP Keeplive if it is a FixNum. If it is hash + # validate he settings are FixNums. + # + # @example With a FixNum + # configure_tcp_keepalive(60) + # + # @example With a Hash + # configure_tcp_keepalive({:time => 30, :intvl => 20, :probes => 2}) + # + # @param [ FixNum ] | [ Hash ] Supply a Fixnum to allow the specific settings to be + # configured for you. Supply a Hash with the :time, :intvl, and :probes + # keys to set specific values + # + # @return [ nil ] nil. + # + # @since 1.0.0 + def configure_tcp_keepalive(keepalive) + case keepalive + when Hash + [:time, :intvl, :probes].each do |key| + unless keepalive[key].is_a?(Fixnum) + raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum" + end + end + when Fixnum + if keepalive >= 60 + keepalive = {:time => keepalive - 20, :intvl => 10, :probes => 2} + + elsif keepalive >= 30 + keepalive = {:time => keepalive - 10, :intvl => 5, :probes => 2} + + elsif keepalive >= 5 + keepalive = {:time => keepalive - 2, :intvl => 2, :probes => 1} + end + end + @keepalive = keepalive + end + # Read data from the socket until we get back the number of bytes that we # are expecting. # @@ -207,63 +247,40 @@ def read_data(socket, length) data end - # Set the tcp_keepalive - # - # @api private - # - # @example With a FixNum. - # set_tcp_keepalive(60) - # - # @example With a Hash - # set_tcp_keepalive({time: 60, intvl: 20, probes: 2}) - # - # @return [ nil ] nil. - # - # @since 2.0.5 if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c} - def set_tcp_keepalive(keepalive) - case keepalive - when Hash - [:time, :intvl, :probes].each do |key| - unless keepalive[key].is_a?(Fixnum) - raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum" - end - end - when Fixnum - if keepalive >= 60 - keepalive = {:time => keepalive - 20, :intvl => 10, :probes => 2} - - elsif keepalive >= 30 - keepalive = {:time => keepalive - 10, :intvl => 5, :probes => 2} - - elsif keepalive >= 5 - keepalive = {:time => keepalive - 2, :intvl => 2, :probes => 1} - end - end - + # Enable the tcp_keepalive + # + # @api private + # + # @example + # enable_tcp_keepalive + # + # @return [ nil ] nil. + # + # @since 2.0.5 + def enable_tcp_keepalive + return unless @keepalive.is_a?(Hash) @sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) - @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive[:time]) - @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive[:intvl]) - @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive[:probes]) - Loggable.debug(" MOPED:", "Configured TCP keepalive for connection with #{keepalive.inspect}", "n/a") - end - - def get_tcp_keepalive - { - :time => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE).int, - :intvl => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL).int, - :probes => @sock.getsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT).int, - } + @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, @keepalive[:time]) + @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, @keepalive[:intvl]) + @sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, @keepalive[:probes]) + Loggable.debug(" MOPED:", "Configured TCP keepalive for connection with #{@keepalive.inspect}", "n/a") end else - def set_tcp_keepalive(keepalive) + # Skeleton method that doesn't enable the tcp_keepalive. + # It simply logs a message saying the feature isn't supported. + # + # @api private + # + # @example With a Hash + # enable_tcp_keepalive + # + # @return [ nil ] nil. + # + # @since 2.0.5 + def enable_tcp_keepalive Loggable.debug(" MOPED:", "Did not configure TCP keepalive for connection as it is not supported on this platform.", "n/a") end - - def get_tcp_keepalive - { - } - end end # Yields a connected socket to the calling back. It will attempt to reconnect From f109604ec82413c189186692f686edcd9f3b7831 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Fri, 29 May 2015 12:19:42 -0500 Subject: [PATCH 7/9] Cleaning up keepalive logic --- lib/moped/connection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index b21f131..4ff1fcd 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -195,7 +195,7 @@ def write(operations) # configured for you. Supply a Hash with the :time, :intvl, and :probes # keys to set specific values # - # @return [ nil ] nil. + # @return The configured keepalive Hash or nil. # # @since 1.0.0 def configure_tcp_keepalive(keepalive) From d8ede06dd43aa04803d9f6c8ca8c399bd5e4388c Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Fri, 29 May 2015 12:23:09 -0500 Subject: [PATCH 8/9] Fixing method name --- lib/moped/connection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 4ff1fcd..8e1c7d1 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -55,7 +55,7 @@ def connect else Socket::TCP.connect(host, port, timeout) end - set_tcp_keepalive + enable_tcp_keepalive end # Is the connection connected? @@ -248,7 +248,7 @@ def read_data(socket, length) end if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| ::Socket.const_defined? c} - # Enable the tcp_keepalive + # Enable the tcp_keepalive if configured # # @api private # From 745dbf063b75c29b82af4c110aa588f9361d1ae0 Mon Sep 17 00:00:00 2001 From: Shawn Stephens Date: Fri, 29 May 2015 12:25:55 -0500 Subject: [PATCH 9/9] Suppress log message stating keepalive not supported if keepalive is not configured. --- lib/moped/connection.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 8e1c7d1..d267e24 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -279,6 +279,7 @@ def enable_tcp_keepalive # # @since 2.0.5 def enable_tcp_keepalive + return unless @keepalive.is_a?(Hash) Loggable.debug(" MOPED:", "Did not configure TCP keepalive for connection as it is not supported on this platform.", "n/a") end end