]> git.saurik.com Git - redis.git/blob - client-libraries/ruby/lib/server.rb
added tests for vararg DEL
[redis.git] / client-libraries / ruby / lib / server.rb
1 begin
2 # Timeout code is courtesy of Ruby memcache-client
3 # http://github.com/mperham/memcache-client/tree
4 # Try to use the SystemTimer gem instead of Ruby's timeout library
5 # when running on something that looks like Ruby 1.8.x. See:
6 # http://ph7spot.com/articles/system_timer
7 # We don't want to bother trying to load SystemTimer on jruby and
8 # ruby 1.9+.
9 if defined?(JRUBY_VERSION) || (RUBY_VERSION >= '1.9')
10 require 'timeout'
11 RedisTimer = Timeout
12 else
13 require 'system_timer'
14 RedisTimer = SystemTimer
15 end
16 rescue LoadError => e
17 puts "[redis-rb] Could not load SystemTimer gem, falling back to Ruby's slower/unsafe timeout library: #{e.message}"
18 require 'timeout'
19 RedisTimer = Timeout
20 end
21
22 ##
23 # This class represents a redis server instance.
24
25 class Server
26
27 ##
28 # The amount of time to wait before attempting to re-establish a
29 # connection with a server that is marked dead.
30
31 RETRY_DELAY = 30.0
32
33 ##
34 # The host the redis server is running on.
35
36 attr_reader :host
37
38 ##
39 # The port the redis server is listening on.
40
41 attr_reader :port
42
43 ##
44 #
45
46 attr_reader :replica
47
48 ##
49 # The time of next retry if the connection is dead.
50
51 attr_reader :retry
52
53 ##
54 # A text status string describing the state of the server.
55
56 attr_reader :status
57
58 ##
59 # Create a new Redis::Server object for the redis instance
60 # listening on the given host and port.
61
62 def initialize(host, port = DEFAULT_PORT, timeout = 10)
63 raise ArgumentError, "No host specified" if host.nil? or host.empty?
64 raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
65
66 @host = host
67 @port = port.to_i
68
69 @sock = nil
70 @retry = nil
71 @status = 'NOT CONNECTED'
72 @timeout = timeout
73 end
74
75 ##
76 # Return a string representation of the server object.
77 def inspect
78 "<Redis::Server: %s:%d (%s)>" % [@host, @port, @status]
79 end
80
81 ##
82 # Try to connect to the redis server targeted by this object.
83 # Returns the connected socket object on success or nil on failure.
84
85 def socket
86 return @sock if @sock and not @sock.closed?
87
88 @sock = nil
89
90 # If the host was dead, don't retry for a while.
91 return if @retry and @retry > Time.now
92
93 # Attempt to connect if not already connected.
94 begin
95 @sock = connect_to(@host, @port, @timeout)
96 @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
97 @retry = nil
98 @status = 'CONNECTED'
99 rescue Errno::EPIPE, Errno::ECONNREFUSED => e
100 puts "Socket died... socket: #{@sock.inspect}\n" if $debug
101 @sock.close
102 retry
103 rescue SocketError, SystemCallError, IOError => err
104 puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
105 mark_dead err
106 end
107 @sock
108 end
109
110 def connect_to(host, port, timeout=nil)
111 socket = TCPSocket.new(host, port, 0)
112 if timeout
113 socket.instance_eval <<-EOR
114 alias :blocking_gets :gets
115 def gets(*args)
116 RedisTimer.timeout(#{timeout}) do
117 self.blocking_gets(*args)
118 end
119 end
120 alias :blocking_read :read
121 def read(*args)
122 RedisTimer.timeout(#{timeout}) do
123 self.blocking_read(*args)
124 end
125 end
126 alias :blocking_write :write
127 def write(*args)
128 RedisTimer.timeout(#{timeout}) do
129 self.blocking_write(*args)
130 end
131 end
132 EOR
133 end
134 socket
135 end
136
137 ##
138 # Close the connection to the redis server targeted by this
139 # object. The server is not considered dead.
140
141 def close
142 @sock.close if @sock && !@sock.closed?
143 @sock = nil
144 @retry = nil
145 @status = "NOT CONNECTED"
146 end
147
148 ##
149 # Mark the server as dead and close its socket.
150 def mark_dead(error)
151 @sock.close if @sock && !@sock.closed?
152 @sock = nil
153 @retry = Time.now #+ RETRY_DELAY
154
155 reason = "#{error.class.name}: #{error.message}"
156 @status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry
157 puts @status
158 end
159
160 end