Imagine, you have created a great module/class for rails interacting with 3rd party service provider over TCP/IP or you build a ruby extension to one of the libraries, where work with IO is done outside of the ruby itself. While you was building it you didn't pay a lot of attention to handling timeouts, network latency, IO blocks or you name it.
And now it is time to go production. Suddenly you find that sometimes the response time from you application is awful, or it seems that process just hanging without any activity (strace shows that it might get stuck in connect or read or write from the IO object, sometimes local sockets with one end only in the netstat at the time of prblem). There could be a tons of reason for connection establishing time to go 'unlimited' - for example - firewall rule drop on the receiver side or writing too much into closed pipe (apache + cronnolog after SIG_HUP serving mp3 or avi) or mis configured switches and routers at UP-link providers playing ping-pong with tcp/ip packets
What's might be a problem? The problem might be that library do not have timeout handling for IO operations.
At this point you have several options - dig into code and place timeout block everywhere or ...
use magic of alias_method_chain
require 'timeout'
module WrapWithTimeout
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def wrap_with_timeout(*actions)
actions.each do |action|
class_eval <<<-EOV
attr_accessor :timeout
def #{action.to_s}_with_timeout(*args, &block)
if timeout.to_f > 0
Timeout::timeout(timeout.to_f){
#{action.to_s}_without_timeout(*args, &block)
}
else
#{action.to_s}_without_timeout(*args, &block)
end
end
alias_method_chain :#{action.to_s}, :timeout
EOV
end
end
end
end
class SuperFunctionalClass
include WrapWithTimeout
def initialize(...)
#may want to add similar line into initialize
self.timeout = 10 #seconds
end
#your methods
def connect_to_provider(a,b,c)
....
end
def write_to_provider(a,b,c)
....
end
def read_from_provider(a,b,c)
....
end
#drop this line
wrap_with_timeout :connect_to_provider, :read_from_provider, :write_from_provider
end
After adding
wrap_with_timeout :connect_to_provider, :read_from_provider, :write_from_provider
all what you need to do is to handle Timeout exception appropriatly.