about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-12-07 02:03:05 +0000
committerEric Wong <normalperson@yhbt.net>2011-12-07 02:29:23 +0000
commit0bbbd8cfa2df665bc22a47831adf91668e391e3e (patch)
tree08b1c1e8cfabaca403ae34bc2b68e4d8510d0610
parentda58f47f541cff65e814a614ecff722ba2a35bf8 (diff)
downloadmogilefs-client-0bbbd8cfa2df665bc22a47831adf91668e391e3e.tar.gz
TCP keepalives are inexpensive, so we can use them to monitor
whether or not our connection is still alive while uploading.

Remote servers make take an unpredictable amount of time to
actually write out the data we've uploaded (and empty socket
buffers to receive more), so it is extremely difficult to
calculate an effective timeout for select() or poll().
-rw-r--r--lib/mogilefs/http_file.rb26
1 files changed, 25 insertions, 1 deletions
diff --git a/lib/mogilefs/http_file.rb b/lib/mogilefs/http_file.rb
index b8bfd48..0d6faf5 100644
--- a/lib/mogilefs/http_file.rb
+++ b/lib/mogilefs/http_file.rb
@@ -1,5 +1,6 @@
 # -*- encoding: binary -*-
 # here are internal implementation details, do not use them in your code
+require 'socket'
 require 'stringio'
 require 'uri'
 require 'digest/md5'
@@ -106,7 +107,7 @@ class MogileFS::HTTPFile < StringIO
   # returns file size if the socket finished writing
   def upload(devid, uri) # :nodoc:
     sock = MogileFS::Socket.tcp(uri.host, uri.port)
-    sock.setsockopt(Socket::IPPROTO_TCP, Socket::SO_KEEPALIVE, 1)
+    set_socket_options(sock)
     file_size = length
 
     if @streaming_io
@@ -192,4 +193,27 @@ class MogileFS::HTTPFile < StringIO
     commit
     super
   end
+
+  # :stopdoc:
+  # aggressive keepalive settings on Linux + Ruby 1.9.2+
+  TCP_KEEPALIVE = {
+    :TCP_KEEPIDLE => 60, # seconds time before keepalive packet is sent
+    :TCP_KEEPINTVL => 5,
+    :TCP_KEEPCNT => 2,  # number of retries
+  }
+
+  req_consts = TCP_KEEPALIVE.keys
+  if (Socket.constants & req_consts).size == req_consts.size
+    def set_socket_options(sock)
+      sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
+      TCP_KEEPALIVE.each do |k,v|
+        sock.setsockopt(:IPPROTO_TCP, k, v)
+      end
+    end
+  else
+    def set_socket_options(sock)
+      sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
+    end
+  end
+  # :startdoc:
 end