winescout-redis 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/redis.rb ADDED
@@ -0,0 +1,462 @@
1
+ require 'socket'
2
+ require 'set'
3
+ require File.join(File.dirname(__FILE__),'server')
4
+
5
+
6
+ class RedisError < StandardError
7
+ end
8
+ class RedisRenameError < StandardError
9
+ end
10
+ class Redis
11
+ ERR = "-".freeze
12
+ OK = 'OK'.freeze
13
+ SINGLE = '+'.freeze
14
+ BULK = '$'.freeze
15
+ MULTI = '*'.freeze
16
+ INT = ':'.freeze
17
+
18
+ attr_reader :server
19
+
20
+
21
+ def initialize(opts={})
22
+ @opts = {:host => 'localhost', :port => '6379'}.merge(opts)
23
+ $debug = @opts[:debug]
24
+ @server = Server.new(@opts[:host], @opts[:port])
25
+ end
26
+
27
+ def to_s
28
+ "#{host}:#{port}"
29
+ end
30
+
31
+ def port
32
+ @opts[:port]
33
+ end
34
+
35
+ def host
36
+ @opts[:host]
37
+ end
38
+
39
+ def with_socket_management(server, &block)
40
+ begin
41
+ block.call(server.socket)
42
+ #Timeout or server down
43
+ rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED => e
44
+ server.close
45
+ puts "Client (#{server.inspect}) disconnected from server: #{e.inspect}\n" if $debug
46
+ retry
47
+ #Server down
48
+ rescue NoMethodError => e
49
+ puts "Client (#{server.inspect}) tryin server that is down: #{e.inspect}\n Dying!" if $debug
50
+ raise Errno::ECONNREFUSED
51
+ #exit
52
+ end
53
+ end
54
+
55
+ def monitor
56
+ ensure_retry do
57
+ with_socket_management(@server) do |socket|
58
+ trap("INT") { puts "\nGot ^C! Dying!"; exit }
59
+ write "MONITOR\r\n"
60
+ puts "Now Monitoring..."
61
+ socket.read(12)
62
+ loop do
63
+ x = socket.gets
64
+ puts x unless x.nil?
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def quit
71
+ write "QUIT\r\n"
72
+ end
73
+
74
+ def select_db(index)
75
+ write "SELECT #{index}\r\n"
76
+ get_response
77
+ end
78
+
79
+ def flush_db
80
+ write "FLUSHDB\r\n"
81
+ get_response == OK
82
+ end
83
+
84
+ def last_save
85
+ write "LASTSAVE\r\n"
86
+ get_response.to_i
87
+ end
88
+
89
+ def bgsave
90
+ write "BGSAVE\r\n"
91
+ get_response == OK
92
+ end
93
+
94
+ def info
95
+ info = {}
96
+ write("INFO\r\n")
97
+ x = get_response
98
+ x.each do |kv|
99
+ k,v = kv.split(':', 2)
100
+ k,v = k.chomp, v = v.chomp
101
+ info[k.to_sym] = v
102
+ end
103
+ info
104
+ end
105
+
106
+
107
+ def bulk_reply
108
+ begin
109
+ x = read.chomp
110
+ puts "bulk_reply read value is #{x.inspect}" if $debug
111
+ return x
112
+ rescue => e
113
+ puts "error in bulk_reply #{e}" if $debug
114
+ nil
115
+ end
116
+ end
117
+
118
+ def write(data)
119
+ with_socket_management(@server) do |socket|
120
+ puts "writing: #{data}" if $debug
121
+ socket.write(data)
122
+ end
123
+ end
124
+
125
+ def fetch(len)
126
+ with_socket_management(@server) do |socket|
127
+ len = [0, len.to_i].max
128
+ res = socket.read(len + 2)
129
+ res = res.chomp if res
130
+ res
131
+ end
132
+ end
133
+
134
+ def read(length = read_proto)
135
+ with_socket_management(@server) do |socket|
136
+ res = socket.read(length)
137
+ puts "read is #{res.inspect}" if $debug
138
+ res
139
+ end
140
+ end
141
+
142
+ def keys(glob)
143
+ write "KEYS #{glob}\r\n"
144
+ get_response.split(' ')
145
+ end
146
+
147
+ def rename!(oldkey, newkey)
148
+ write "RENAME #{oldkey} #{newkey}\r\n"
149
+ get_response
150
+ end
151
+
152
+ def rename(oldkey, newkey)
153
+ write "RENAMENX #{oldkey} #{newkey}\r\n"
154
+ case get_response
155
+ when -1
156
+ raise RedisRenameError, "source key: #{oldkey} does not exist"
157
+ when 0
158
+ raise RedisRenameError, "target key: #{oldkey} already exists"
159
+ when -3
160
+ raise RedisRenameError, "source and destination keys are the same"
161
+ when 1
162
+ true
163
+ end
164
+ end
165
+
166
+ def key?(key)
167
+ write "EXISTS #{key}\r\n"
168
+ get_response == 1
169
+ end
170
+
171
+ def delete(key)
172
+ write "DEL #{key}\r\n"
173
+ get_response == 1
174
+ end
175
+
176
+ def [](key)
177
+ get(key)
178
+ end
179
+
180
+ def get(key)
181
+ write "GET #{key}\r\n"
182
+ get_response
183
+ end
184
+
185
+ def mget(*keys)
186
+ write "MGET #{keys.join(' ')}\r\n"
187
+ get_response
188
+ end
189
+
190
+ def incr(key, increment=nil)
191
+ if increment
192
+ write "INCRBY #{key} #{increment}\r\n"
193
+ else
194
+ write "INCR #{key}\r\n"
195
+ end
196
+ get_response
197
+ end
198
+
199
+ def decr(key, decrement=nil)
200
+ if decrement
201
+ write "DECRRBY #{key} #{decrement}\r\n"
202
+ else
203
+ write "DECR #{key}\r\n"
204
+ end
205
+ get_response
206
+ end
207
+
208
+ def randkey
209
+ write "RANDOMKEY\r\n"
210
+ get_response
211
+ end
212
+
213
+ def list_length(key)
214
+ write "LLEN #{key}\r\n"
215
+ case i = get_response
216
+ when -2
217
+ raise RedisError, "key: #{key} does not hold a list value"
218
+ else
219
+ i
220
+ end
221
+ end
222
+
223
+ def type?(key)
224
+ write "TYPE #{key}\r\n"
225
+ get_response
226
+ end
227
+
228
+ def push_tail(key, string)
229
+ write "RPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
230
+ get_response
231
+ end
232
+
233
+ def push_head(key, string)
234
+ write "LPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
235
+ get_response
236
+ end
237
+
238
+ def pop_head(key)
239
+ write "LPOP #{key}\r\n"
240
+ get_response
241
+ end
242
+
243
+ def pop_tail(key)
244
+ write "RPOP #{key}\r\n"
245
+ get_response
246
+ end
247
+
248
+ def list_set(key, index, val)
249
+ write "LSET #{key} #{index} #{val.to_s.size}\r\n#{val}\r\n"
250
+ get_response == OK
251
+ end
252
+
253
+ def list_length(key)
254
+ write "LLEN #{key}\r\n"
255
+ case i = get_response
256
+ when -2
257
+ raise RedisError, "key: #{key} does not hold a list value"
258
+ else
259
+ i
260
+ end
261
+ end
262
+
263
+ def list_range(key, start, ending)
264
+ write "LRANGE #{key} #{start} #{ending}\r\n"
265
+ get_response
266
+ end
267
+
268
+ def list_trim(key, start, ending)
269
+ write "LTRIM #{key} #{start} #{ending}\r\n"
270
+ get_response
271
+ end
272
+
273
+ def list_index(key, index)
274
+ write "LINDEX #{key} #{index}\r\n"
275
+ get_response
276
+ end
277
+
278
+ def list_rm(key, count, value)
279
+ write "LREM #{key} #{count} #{value.to_s.size}\r\n#{value}\r\n"
280
+ case num = get_response
281
+ when -1
282
+ raise RedisError, "key: #{key} does not exist"
283
+ when -2
284
+ raise RedisError, "key: #{key} does not hold a list value"
285
+ else
286
+ num
287
+ end
288
+ end
289
+
290
+ def set_add(key, member)
291
+ write "SADD #{key} #{member.to_s.size}\r\n#{member}\r\n"
292
+ case get_response
293
+ when 1
294
+ true
295
+ when 0
296
+ false
297
+ when -2
298
+ raise RedisError, "key: #{key} contains a non set value"
299
+ end
300
+ end
301
+
302
+ def set_delete(key, member)
303
+ write "SREM #{key} #{member.to_s.size}\r\n#{member}\r\n"
304
+ case get_response
305
+ when 1
306
+ true
307
+ when 0
308
+ false
309
+ when -2
310
+ raise RedisError, "key: #{key} contains a non set value"
311
+ end
312
+ end
313
+
314
+ def set_count(key)
315
+ write "SCARD #{key}\r\n"
316
+ case i = get_response
317
+ when -2
318
+ raise RedisError, "key: #{key} contains a non set value"
319
+ else
320
+ i
321
+ end
322
+ end
323
+
324
+ def set_member?(key, member)
325
+ write "SISMEMBER #{key} #{member.to_s.size}\r\n#{member}\r\n"
326
+ case get_response
327
+ when 1
328
+ true
329
+ when 0
330
+ false
331
+ when -2
332
+ raise RedisError, "key: #{key} contains a non set value"
333
+ end
334
+ end
335
+
336
+ def set_members(key)
337
+ write "SMEMBERS #{key}\r\n"
338
+ Set.new(get_response)
339
+ end
340
+
341
+ def set_intersect(*keys)
342
+ write "SINTER #{keys.join(' ')}\r\n"
343
+ Set.new(get_response)
344
+ end
345
+
346
+ def set_inter_store(destkey, *keys)
347
+ write "SINTERSTORE #{destkey} #{keys.join(' ')}\r\n"
348
+ get_response
349
+ end
350
+
351
+ def sort(key, opts={})
352
+ cmd = "SORT #{key}"
353
+ cmd << " BY #{opts[:by]}" if opts[:by]
354
+ cmd << " GET #{opts[:get]}" if opts[:get]
355
+ cmd << " INCR #{opts[:incr]}" if opts[:incr]
356
+ cmd << " DEL #{opts[:del]}" if opts[:del]
357
+ cmd << " DECR #{opts[:decr]}" if opts[:decr]
358
+ cmd << " #{opts[:order]}" if opts[:order]
359
+ cmd << " LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
360
+ cmd << "\r\n"
361
+ write(cmd)
362
+ get_response
363
+ end
364
+
365
+ def multi_bulk
366
+ res = read_proto
367
+ puts "mb res is #{res.inspect}" if $debug
368
+ list = []
369
+ Integer(res).times do
370
+ vf = get_response
371
+ puts "curren vf is #{vf.inspect}" if $debug
372
+ list << vf
373
+ puts "current list is #{list.inspect}" if $debug
374
+ end
375
+ list
376
+ end
377
+
378
+ def get_reply
379
+ begin
380
+ r = read(1)
381
+ raise RedisError if (r == "\r" || r == "\n")
382
+ rescue RedisError
383
+ retry
384
+ end
385
+ r
386
+ end
387
+
388
+ def []=(key, val)
389
+ set(key,val)
390
+ end
391
+
392
+
393
+ def set(key, val, expiry=nil)
394
+ write("SET #{key} #{val.to_s.size}\r\n#{val}\r\n")
395
+ get_response == OK
396
+ end
397
+
398
+ def set_unless_exists(key, val)
399
+ write "SETNX #{key} #{val.to_s.size}\r\n#{val}\r\n"
400
+ get_response == 1
401
+ end
402
+
403
+ def status_code_reply
404
+ begin
405
+ res = read_proto
406
+ if res.index('-') == 0
407
+ raise RedisError, res
408
+ else
409
+ true
410
+ end
411
+ rescue RedisError
412
+ raise RedisError
413
+ end
414
+ end
415
+
416
+ def get_response
417
+ begin
418
+ rtype = get_reply
419
+ rescue => e
420
+ raise RedisError, e.inspect
421
+ end
422
+ puts "reply_type is #{rtype.inspect}" if $debug
423
+ case rtype
424
+ when SINGLE
425
+ single_line
426
+ when BULK
427
+ bulk_reply
428
+ when MULTI
429
+ multi_bulk
430
+ when INT
431
+ integer_reply
432
+ when ERR
433
+ raise RedisError, single_line
434
+ else
435
+ raise RedisError, "Unknown response.."
436
+ end
437
+ end
438
+
439
+ def integer_reply
440
+ Integer(read_proto)
441
+ end
442
+
443
+ def single_line
444
+ buff = ""
445
+ while buff[-2..-1] != "\r\n"
446
+ buff << read(1)
447
+ end
448
+ puts "single_line value is #{buff[0..-3].inspect}" if $debug
449
+ buff[0..-3]
450
+ end
451
+
452
+ def read_proto
453
+ with_socket_management(@server) do |socket|
454
+ if res = socket.gets
455
+ x = res.chomp
456
+ puts "read_proto is #{x.inspect}\n\n" if $debug
457
+ x.to_i
458
+ end
459
+ end
460
+ end
461
+
462
+ end
data/lib/server.rb ADDED
@@ -0,0 +1,131 @@
1
+ ##
2
+ # This class represents a redis server instance.
3
+
4
+ class Server
5
+
6
+ ##
7
+ # The amount of time to wait before attempting to re-establish a
8
+ # connection with a server that is marked dead.
9
+
10
+ RETRY_DELAY = 30.0
11
+
12
+ ##
13
+ # The host the redis server is running on.
14
+
15
+ attr_reader :host
16
+
17
+ ##
18
+ # The port the redis server is listening on.
19
+
20
+ attr_reader :port
21
+
22
+ ##
23
+ #
24
+
25
+ attr_reader :replica
26
+
27
+ ##
28
+ # The time of next retry if the connection is dead.
29
+
30
+ attr_reader :retry
31
+
32
+ ##
33
+ # A text status string describing the state of the server.
34
+
35
+ attr_reader :status
36
+
37
+ ##
38
+ # Create a new Redis::Server object for the redis instance
39
+ # listening on the given host and port.
40
+
41
+ def initialize(host, port = DEFAULT_PORT)
42
+ raise ArgumentError, "No host specified" if host.nil? or host.empty?
43
+ raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
44
+
45
+ @host = host
46
+ @port = port.to_i
47
+
48
+ @sock = nil
49
+ @retry = nil
50
+ @status = 'NOT CONNECTED'
51
+ @timeout = 1
52
+ end
53
+
54
+ ##
55
+ # Return a string representation of the server object.
56
+ def inspect
57
+ "<Redis::Server: %s:%d (%s)>" % [@host, @port, @status]
58
+ end
59
+
60
+ ##
61
+ # Try to connect to the redis server targeted by this object.
62
+ # Returns the connected socket object on success or nil on failure.
63
+
64
+ def socket
65
+ return @sock if @sock and not @sock.closed?
66
+
67
+ @sock = nil
68
+
69
+ # If the host was dead, don't retry for a while.
70
+ return if @retry and @retry > Time.now
71
+
72
+ # Attempt to connect if not already connected.
73
+ begin
74
+ @sock = connect_to(@host, @port, @timeout)
75
+ @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
76
+ @retry = nil
77
+ @status = 'CONNECTED'
78
+ rescue Errno::EPIPE, Errno::ECONNREFUSED => e
79
+ puts "Socket died... socket: #{@sock.inspect}\n" if $debug
80
+ @sock.close
81
+ retry
82
+ rescue SocketError, SystemCallError, IOError => err
83
+ puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
84
+ mark_dead err
85
+ end
86
+
87
+ return @sock
88
+ end
89
+
90
+ def connect_to(host, port, timeout=nil)
91
+ addrs = Socket.getaddrinfo(host, nil)
92
+ addr = addrs.detect { |ad| ad[0] == 'AF_INET' }
93
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
94
+ #addr = Socket.getaddrinfo(host, nil)
95
+ #sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
96
+
97
+ if timeout
98
+ secs = Integer(timeout)
99
+ usecs = Integer((timeout - secs) * 1_000_000)
100
+ optval = [secs, usecs].pack("l_2")
101
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
102
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
103
+ end
104
+ sock.connect(Socket.pack_sockaddr_in(port, addr[3]))
105
+ sock
106
+ end
107
+
108
+ ##
109
+ # Close the connection to the redis server targeted by this
110
+ # object. The server is not considered dead.
111
+
112
+ def close
113
+ @sock.close if @sock && !@sock.closed?
114
+ @sock = nil
115
+ @retry = nil
116
+ @status = "NOT CONNECTED"
117
+ end
118
+
119
+ ##
120
+ # Mark the server as dead and close its socket.
121
+ def mark_dead(error)
122
+ @sock.close if @sock && !@sock.closed?
123
+ @sock = nil
124
+ @retry = Time.now #+ RETRY_DELAY
125
+
126
+ reason = "#{error.class.name}: #{error.message}"
127
+ @status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry
128
+ puts @status
129
+ end
130
+
131
+ end