oxblood 0.1.0.dev6 → 0.1.0.dev7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -25
- data/benchmarks/pipeline.rb +36 -0
- data/lib/oxblood/buffered_io.rb +0 -34
- data/lib/oxblood/connection.rb +28 -100
- data/lib/oxblood/pipeline.rb +2 -1
- data/lib/oxblood/pool.rb +7 -3
- data/lib/oxblood/rsocket.rb +131 -0
- data/lib/oxblood/session.rb +253 -10
- data/lib/oxblood/version.rb +1 -1
- data/lib/redis/connection/oxblood.rb +5 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 007983f13baaa6978b6db2a3fe4fc821abaa8f56
|
4
|
+
data.tar.gz: e96fbcaca9bcb69f95ddbae0d2ae606bf8fc0954
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 553a57f8b85f9407758c60cab222bbbfcc7804d74d1daad993bd46aec2099449159a3f8911bcb55e4957a62ae64d945ef7e6064bb121035ed7f09f683aa95b25
|
7
|
+
data.tar.gz: 0f7a5078313ffde39c70c4ffc2a87089881fcf4e8968591eaaf35bf4ee93c139a505e6572c924afb59eed75fe0e9308a78692c9a9e8fdac7d3f753116885e4c6
|
data/README.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/oxblood.svg)](https://badge.fury.io/rb/oxblood)
|
4
4
|
[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/etehtsea/oxblood/master/frames)
|
5
|
+
[![Build Status](https://travis-ci.org/etehtsea/oxblood.svg?branch=master)](https://travis-ci.org/etehtsea/oxblood)
|
5
6
|
[![Code Climate](https://codeclimate.com/github/etehtsea/oxblood/badges/gpa.svg)](https://codeclimate.com/github/etehtsea/oxblood)
|
6
7
|
[![Test Coverage](https://codeclimate.com/github/etehtsea/oxblood/badges/coverage.svg)](https://codeclimate.com/github/etehtsea/oxblood/coverage)
|
7
|
-
[![Issue Count](https://codeclimate.com/github/etehtsea/oxblood/badges/issue_count.svg)](https://codeclimate.com/github/etehtsea/oxblood)
|
8
8
|
|
9
9
|
An experimental Redis Ruby client.
|
10
10
|
|
@@ -13,34 +13,34 @@ An experimental Redis Ruby client.
|
|
13
13
|
- Ruby 2.2.2+
|
14
14
|
- JRuby 9k+
|
15
15
|
|
16
|
+
## Status
|
17
|
+
|
18
|
+
- Commands:
|
19
|
+
- Cluster (0/20)
|
20
|
+
- Connection (4/5)
|
21
|
+
- Geo (0/6)
|
22
|
+
- Hashes (14/15)
|
23
|
+
- HyperLogLog (0/3)
|
24
|
+
- Keys (18/22)
|
25
|
+
- Lists (6/17)
|
26
|
+
- Pub/Sub (0/6)
|
27
|
+
- Scripting (0/7)
|
28
|
+
- Server (2/31)
|
29
|
+
- Sets (5/15)
|
30
|
+
- Sorted Sets (6/21)
|
31
|
+
- Strings (5/24)
|
32
|
+
- Transaction (0/5)
|
33
|
+
- [Pipeling](http://www.rubydoc.info/github/etehtsea/oxblood/master/Oxblood/Pipeline)
|
34
|
+
- [Connection pooling](http://www.rubydoc.info/github/etehtsea/oxblood/master/Oxblood/Pool)
|
35
|
+
|
16
36
|
## Usage
|
37
|
+
As a starting point please look at [Oxblood::Pool](http://www.rubydoc.info/github/etehtsea/oxblood/master/Oxblood/Pool) documentation.
|
17
38
|
|
18
|
-
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
require 'oxblood'
|
22
|
-
pool = Oxblood::Pool.new(size: 8)
|
23
|
-
pool.with { |c| c.ping }
|
24
|
-
```
|
25
|
-
|
26
|
-
### As [redis-rb](https://github.com/redis/redis-rb) driver
|
27
|
-
|
28
|
-
```ruby
|
29
|
-
[1] pry(main)> require 'redis/connection/oxblood'
|
30
|
-
=> true
|
31
|
-
[2] pry(main)> require 'redis'
|
32
|
-
=> true
|
33
|
-
# For implicit usage connection should be required before redis gem
|
34
|
-
[3] pry(main)> Redis.new.client.options[:driver]
|
35
|
-
=> Redis::Connection::Oxblood
|
36
|
-
# Explicitly
|
37
|
-
[4] pry(main)> Redis.new(driver: :oxblood).client.options[:driver]
|
38
|
-
=> Redis::Connection::Oxblood
|
39
|
-
```
|
39
|
+
## Documentation
|
40
|
+
Documentation and usage examples are available on [Rubydoc](http://rubydoc.info/github/etehtsea/oxblood/master/frames).
|
40
41
|
|
41
42
|
## Continuous Integration
|
42
|
-
|
43
|
-
[![Build Status](https://travis-ci.org/etehtsea/oxblood.svg?branch=master)](https://travis-ci.org/etehtsea/oxblood)
|
43
|
+
You can check CI status at [Travis CI](https://travis-ci.org/etehtsea/oxblood.svg?branch=master).
|
44
44
|
|
45
45
|
## Contributing
|
46
46
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'oxblood'
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
N = 100_000
|
6
|
+
|
7
|
+
def benchmark(label, &blk)
|
8
|
+
sec = Benchmark.realtime(&blk)
|
9
|
+
puts [label, sec.round(3)].join(': ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def redis_without
|
13
|
+
r = Redis.new
|
14
|
+
N.times { r.ping }
|
15
|
+
end
|
16
|
+
|
17
|
+
def redis_with
|
18
|
+
r = Redis.new
|
19
|
+
r.pipelined { N.times { r.ping } }
|
20
|
+
end
|
21
|
+
|
22
|
+
def oxblood_without
|
23
|
+
r = Oxblood::Session.new(Oxblood::Connection.new)
|
24
|
+
N.times { r.ping }
|
25
|
+
end
|
26
|
+
|
27
|
+
def oxblood_with
|
28
|
+
pipe = Oxblood::Pipeline.new(Oxblood::Connection.new)
|
29
|
+
N.times { pipe.ping }
|
30
|
+
pipe.sync
|
31
|
+
end
|
32
|
+
|
33
|
+
benchmark('redis without') { redis_without }
|
34
|
+
benchmark('redis with') { redis_with }
|
35
|
+
benchmark('oxblood without') { oxblood_without }
|
36
|
+
benchmark('oxblood with') { oxblood_with }
|
data/lib/oxblood/buffered_io.rb
CHANGED
@@ -6,41 +6,7 @@ module Oxblood
|
|
6
6
|
@buffer = String.new
|
7
7
|
end
|
8
8
|
|
9
|
-
def gets(separator, timeout)
|
10
|
-
crlf = nil
|
11
|
-
|
12
|
-
while (crlf = @buffer.index(separator)) == nil
|
13
|
-
@buffer << _read_from_socket(1024, timeout)
|
14
|
-
end
|
15
|
-
|
16
|
-
@buffer.slice!(0, crlf + separator.bytesize)
|
17
|
-
end
|
18
|
-
|
19
|
-
def read(nbytes, timeout)
|
20
|
-
result = @buffer.slice!(0, nbytes)
|
21
|
-
|
22
|
-
while result.bytesize < nbytes
|
23
|
-
result << _read_from_socket(nbytes - result.bytesize, timeout)
|
24
|
-
end
|
25
|
-
|
26
|
-
result
|
27
|
-
end
|
28
9
|
|
29
10
|
private
|
30
|
-
|
31
|
-
def _read_from_socket(nbytes, timeout)
|
32
|
-
begin
|
33
|
-
@socket.read_nonblock(nbytes)
|
34
|
-
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
35
|
-
if IO.select([@socket], nil, nil, timeout)
|
36
|
-
retry
|
37
|
-
else
|
38
|
-
raise Connection::TimeoutError
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
rescue EOFError
|
43
|
-
raise Errno::ECONNRESET
|
44
|
-
end
|
45
11
|
end
|
46
12
|
end
|
data/lib/oxblood/connection.rb
CHANGED
@@ -1,65 +1,33 @@
|
|
1
1
|
require 'oxblood/protocol'
|
2
|
-
require 'oxblood/
|
2
|
+
require 'oxblood/rsocket'
|
3
3
|
require 'oxblood/session'
|
4
4
|
|
5
5
|
module Oxblood
|
6
6
|
# Class responsible for connection maintenance
|
7
7
|
class Connection
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
port = opts.fetch(:port, 6379)
|
32
|
-
connect_timeout = opts.fetch(:connect_timeout, 1.0)
|
33
|
-
|
34
|
-
tcp_socket(host, port, connect_timeout)
|
35
|
-
end
|
36
|
-
|
37
|
-
timeout = opts.fetch(:timeout, 1.0)
|
38
|
-
|
39
|
-
new(socket, timeout).tap do |conn|
|
40
|
-
session = Session.new(conn)
|
41
|
-
session.auth!(opts[:password]) if opts[:password]
|
42
|
-
session.select(opts[:db]) if opts[:db]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def unix_socket(path)
|
49
|
-
UNIXSocket.new(path)
|
50
|
-
end
|
51
|
-
|
52
|
-
def tcp_socket(host, port, connect_timeout)
|
53
|
-
Socket.tcp(host, port, connect_timeout: connect_timeout).tap do |sock|
|
54
|
-
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def initialize(socket, timeout)
|
60
|
-
@socket = socket
|
61
|
-
@timeout = timeout
|
62
|
-
@buffer = BufferedIO.new(socket)
|
8
|
+
# @!attribute [r] socket
|
9
|
+
# @return [RSocket] resilient socket
|
10
|
+
attr_reader :socket
|
11
|
+
|
12
|
+
# Initialize connection to Redis server
|
13
|
+
#
|
14
|
+
# @param [Hash] opts Connection options
|
15
|
+
#
|
16
|
+
# @option opts [Float] :timeout (1.0) socket read/write timeout
|
17
|
+
# @option opts [Integer] :db database number
|
18
|
+
# @option opts [String] :password
|
19
|
+
#
|
20
|
+
# @option opts [String] :host ('localhost') Hostname or IP address to connect to
|
21
|
+
# @option opts [Integer] :port (6379) Port Redis server listens on
|
22
|
+
# @option opts [Float] :connect_timeout (1.0) socket connect timeout
|
23
|
+
#
|
24
|
+
# @option opts [String] :path UNIX socket path
|
25
|
+
def initialize(opts = {})
|
26
|
+
@socket = RSocket.new(opts)
|
27
|
+
|
28
|
+
session = Session.new(self)
|
29
|
+
session.auth!(opts[:password]) if opts[:password]
|
30
|
+
session.select(opts[:db]) if opts[:db]
|
63
31
|
end
|
64
32
|
|
65
33
|
# Send comand to Redis server
|
@@ -67,14 +35,7 @@ module Oxblood
|
|
67
35
|
# @param [Array] command Array of command name with it's args
|
68
36
|
# @return [Integer] Number of bytes written to socket
|
69
37
|
def send_command(*command)
|
70
|
-
write(Protocol.build_command(*command))
|
71
|
-
end
|
72
|
-
|
73
|
-
# Write data to socket
|
74
|
-
# @param [#to_s] data given
|
75
|
-
# @return [Integer] the number of bytes written
|
76
|
-
def write(data)
|
77
|
-
@socket.write(data)
|
38
|
+
@socket.write(Protocol.build_command(*command))
|
78
39
|
end
|
79
40
|
|
80
41
|
# Send command to Redis server and read response from it
|
@@ -85,47 +46,14 @@ module Oxblood
|
|
85
46
|
read_response
|
86
47
|
end
|
87
48
|
|
88
|
-
# True if connection is established
|
89
|
-
# @return [Boolean] connection status
|
90
|
-
def connected?
|
91
|
-
!!@socket
|
92
|
-
end
|
93
|
-
|
94
|
-
# Close connection to server
|
95
|
-
def close
|
96
|
-
@socket.close
|
97
|
-
ensure
|
98
|
-
@socket = nil
|
99
|
-
end
|
100
|
-
|
101
|
-
# Read number of bytes
|
102
|
-
# @param [Integer] nbytes number of bytes to read
|
103
|
-
# @return [String] read result
|
104
|
-
def read(nbytes)
|
105
|
-
@buffer.read(nbytes, @timeout)
|
106
|
-
end
|
107
|
-
|
108
|
-
# Read until separator
|
109
|
-
# @param [String] sep separator
|
110
|
-
# @return [String] read result
|
111
|
-
def gets(sep)
|
112
|
-
@buffer.gets(sep, @timeout)
|
113
|
-
end
|
114
|
-
|
115
|
-
# Set new read timeout
|
116
|
-
# @param [Float] timeout new timeout
|
117
|
-
def timeout=(timeout)
|
118
|
-
@timeout = timeout
|
119
|
-
end
|
120
|
-
|
121
49
|
# Read response from server
|
122
|
-
# @raise [TimeoutError] if timeout happen
|
123
|
-
# @note Will raise TimeoutError even if there is simply no response to read
|
50
|
+
# @raise [RSocket::TimeoutError] if timeout happen
|
51
|
+
# @note Will raise RSocket::TimeoutError even if there is simply no response to read
|
124
52
|
# from server. For example, if you are trying to read response before
|
125
53
|
# sending command.
|
126
54
|
# @todo Raise specific error if server has nothing to answer.
|
127
55
|
def read_response
|
128
|
-
Protocol.parse(
|
56
|
+
Protocol.parse(@socket)
|
129
57
|
end
|
130
58
|
|
131
59
|
# Read several responses from server
|
data/lib/oxblood/pipeline.rb
CHANGED
@@ -20,7 +20,8 @@ module Oxblood
|
|
20
20
|
# @return [Array] of responses
|
21
21
|
def sync
|
22
22
|
serialized_commands = @commands.map { |c| serialize(*c) }
|
23
|
-
|
23
|
+
|
24
|
+
@connection.socket.write(serialized_commands.join)
|
24
25
|
@connection.read_responses(@commands.size)
|
25
26
|
end
|
26
27
|
|
data/lib/oxblood/pool.rb
CHANGED
@@ -4,7 +4,11 @@ require 'oxblood/pipeline'
|
|
4
4
|
require 'oxblood/connection'
|
5
5
|
|
6
6
|
module Oxblood
|
7
|
-
#
|
7
|
+
# Create connection pool. For the most use cases this is entrypoint API.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# pool = Oxblood::Pool.new(size: 8)
|
11
|
+
# pool.with { |c| c.ping } # => 'PONG'
|
8
12
|
class Pool
|
9
13
|
# Initialize connection pool
|
10
14
|
#
|
@@ -12,13 +16,13 @@ module Oxblood
|
|
12
16
|
#
|
13
17
|
# @option options [Float] :timeout (1.0) Connection acquisition timeout.
|
14
18
|
# @option options [Integer] :size Pool size.
|
15
|
-
# @option options [Hash] :connection see {Connection
|
19
|
+
# @option options [Hash] :connection see {Connection#initialize}
|
16
20
|
def initialize(options = {})
|
17
21
|
timeout = options.fetch(:timeout, 1.0)
|
18
22
|
size = options.fetch(:size)
|
19
23
|
|
20
24
|
@pool = ConnectionPool.new(size: size, timeout: timeout) do
|
21
|
-
Connection.
|
25
|
+
Connection.new(options.fetch(:connection, {}))
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'io/wait'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module Oxblood
|
5
|
+
# Thin socket wrapper made with resilience. Socket will be closed and
|
6
|
+
# automatically recreated in case of any errors (including timeout errors)
|
7
|
+
# in order to avoid inconsistent state.
|
8
|
+
class RSocket
|
9
|
+
TimeoutError = Class.new(RuntimeError)
|
10
|
+
|
11
|
+
# @!attribute [rw] timeout
|
12
|
+
# @return [Numeric] timeout in seconds
|
13
|
+
attr_accessor :timeout
|
14
|
+
|
15
|
+
# Maintain socket
|
16
|
+
#
|
17
|
+
# @param [Hash] opts Connection options
|
18
|
+
#
|
19
|
+
# @option opts [Float] :timeout (1.0) socket read/write timeout
|
20
|
+
#
|
21
|
+
# @option opts [String] :host ('localhost') Hostname or IP address to connect to
|
22
|
+
# @option opts [Integer] :port (6379) Port Redis server listens on
|
23
|
+
# @option opts [Float] :connect_timeout (1.0) socket connect timeout
|
24
|
+
#
|
25
|
+
# @option opts [String] :path UNIX socket path
|
26
|
+
def initialize(opts = {})
|
27
|
+
@opts = opts
|
28
|
+
@timeout = opts.fetch(:timeout, 1.0)
|
29
|
+
@socket = create_socket(opts)
|
30
|
+
@buffer = String.new.encode!('ASCII-8BIT')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Read number of bytes
|
34
|
+
# @param [Integer] nbytes number of bytes to read
|
35
|
+
# @return [String] read result
|
36
|
+
def read(nbytes)
|
37
|
+
result = @buffer.slice!(0, nbytes)
|
38
|
+
|
39
|
+
while result.bytesize < nbytes
|
40
|
+
result << readpartial(nbytes - result.bytesize)
|
41
|
+
end
|
42
|
+
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Read until separator
|
47
|
+
# @param [String] separator separator
|
48
|
+
# @return [String] read result
|
49
|
+
def gets(separator)
|
50
|
+
while (crlf = @buffer.index(separator)).nil?
|
51
|
+
@buffer << readpartial(1024)
|
52
|
+
end
|
53
|
+
|
54
|
+
@buffer.slice!(0, crlf + separator.bytesize)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Write data to socket
|
58
|
+
# @param [String] data given
|
59
|
+
# @return [Integer] the number of bytes written
|
60
|
+
def write(data)
|
61
|
+
full_size = data.bytesize
|
62
|
+
|
63
|
+
while data.bytesize > 0
|
64
|
+
written = socket.write_nonblock(data, exception: false)
|
65
|
+
|
66
|
+
if written == :wait_writable
|
67
|
+
socket.wait_writable(@timeout) or fail_with_timeout!
|
68
|
+
else
|
69
|
+
data = data.byteslice(written..-1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
full_size
|
74
|
+
end
|
75
|
+
|
76
|
+
# Close connection to server
|
77
|
+
# @return [nil] always return nil
|
78
|
+
def close
|
79
|
+
@buffer.clear
|
80
|
+
socket.close
|
81
|
+
rescue IOError
|
82
|
+
;
|
83
|
+
ensure
|
84
|
+
@socket = nil
|
85
|
+
end
|
86
|
+
|
87
|
+
# True if connection is established
|
88
|
+
# @return [Boolean] connection status
|
89
|
+
def connected?
|
90
|
+
!!@socket
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def socket
|
96
|
+
@socket ||= create_socket(@opts)
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_socket(opts)
|
100
|
+
if opts.key?(:path)
|
101
|
+
UNIXSocket.new(opts.fetch(:path))
|
102
|
+
else
|
103
|
+
host = opts.fetch(:host, 'localhost')
|
104
|
+
port = opts.fetch(:port, 6379)
|
105
|
+
connect_timeout = opts.fetch(:connect_timeout, 1.0)
|
106
|
+
|
107
|
+
Socket.tcp(host, port, connect_timeout: connect_timeout).tap do |sock|
|
108
|
+
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def readpartial(nbytes)
|
114
|
+
case data = socket.read_nonblock(nbytes, exception: false)
|
115
|
+
when String
|
116
|
+
return data
|
117
|
+
when :wait_readable
|
118
|
+
socket.wait_readable(@timeout) or fail_with_timeout!
|
119
|
+
when nil
|
120
|
+
close
|
121
|
+
raise Errno::ECONNRESET
|
122
|
+
end while true
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def fail_with_timeout!
|
127
|
+
close
|
128
|
+
raise TimeoutError
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/oxblood/session.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
module Oxblood
|
2
|
+
# Implements usual Request/Response protocol
|
3
|
+
#
|
4
|
+
# @note {Session} don't maintain threadsafety! In multithreaded environment
|
5
|
+
# please use {Pool}
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# conn = Oxblood::Connection.new
|
9
|
+
# session = Oxblood::Session.new(conn)
|
10
|
+
# session.ping # => 'PONG'
|
2
11
|
class Session
|
3
12
|
def initialize(connection)
|
4
13
|
@connection = connection
|
@@ -174,9 +183,70 @@ module Oxblood
|
|
174
183
|
run(:HVALS, key)
|
175
184
|
end
|
176
185
|
|
177
|
-
#
|
186
|
+
#
|
187
|
+
# Strings
|
188
|
+
#
|
189
|
+
|
190
|
+
# Get the value of a key
|
191
|
+
# @see http://redis.io/commands/get
|
192
|
+
#
|
193
|
+
# @param [String] key
|
194
|
+
#
|
195
|
+
# @return [String, nil] the value of key, or nil when key does not exists
|
196
|
+
def get(key)
|
197
|
+
run(:GET, key)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Increment the integer value of a key by one
|
201
|
+
# @see http://redis.io/commands/incr
|
202
|
+
#
|
203
|
+
# @param [String] key
|
204
|
+
#
|
205
|
+
# @return [Integer] the value of key after the increment
|
206
|
+
# @return [RError] if the key contains a value of the wrong type or contains
|
207
|
+
# a string that can not be represented as integer
|
208
|
+
def incr(key)
|
209
|
+
run(:INCR, key)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Increment the integer value of a key by the given amount
|
213
|
+
# @see http://redis.io/commands/incrby
|
214
|
+
#
|
215
|
+
# @param [String] key
|
216
|
+
# @param [Integer] increment
|
217
|
+
#
|
218
|
+
# @return [Integer] the value of key after the increment
|
219
|
+
def incrby(key, increment)
|
220
|
+
run(:INCRBY, key, increment)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Get the values of all the given keys
|
224
|
+
# @see http://redis.io/commands/mget
|
225
|
+
#
|
226
|
+
# @param [Array<String>] keys to retrieve
|
227
|
+
#
|
228
|
+
# @return [Array] list of values at the specified keys
|
229
|
+
def mget(*keys)
|
230
|
+
run(*keys.unshift(:MGET))
|
231
|
+
end
|
232
|
+
|
233
|
+
# Set the string value of a key
|
234
|
+
# @see http://redis.io/commands/set
|
235
|
+
#
|
236
|
+
# @todo Add support for set options
|
237
|
+
# http://redis.io/commands/set#options
|
238
|
+
#
|
239
|
+
# @param [String] key
|
240
|
+
# @param [String] value
|
241
|
+
#
|
242
|
+
# @return [String] 'OK' if SET was executed correctly
|
243
|
+
def set(key, value)
|
244
|
+
run(:SET, key, value)
|
245
|
+
end
|
178
246
|
|
179
|
-
#
|
247
|
+
#
|
248
|
+
# Connection
|
249
|
+
#
|
180
250
|
|
181
251
|
# Authenticate to the server
|
182
252
|
# @see http://redis.io/commands/auth
|
@@ -234,7 +304,17 @@ module Oxblood
|
|
234
304
|
run(:SELECT, index)
|
235
305
|
end
|
236
306
|
|
237
|
-
#
|
307
|
+
#
|
308
|
+
# Server
|
309
|
+
#
|
310
|
+
|
311
|
+
# Remove all keys from the current database
|
312
|
+
# @see http://redis.io/commands/flushdb
|
313
|
+
#
|
314
|
+
# @return [String] should always return 'OK'
|
315
|
+
def flushdb
|
316
|
+
run(:FLUSHDB)
|
317
|
+
end
|
238
318
|
|
239
319
|
# Returns information and statistics about the server in a format that is
|
240
320
|
# simple to parse by computers and easy to read by humans
|
@@ -247,7 +327,9 @@ module Oxblood
|
|
247
327
|
section ? run(:INFO, section) : run(:INFO)
|
248
328
|
end
|
249
329
|
|
250
|
-
#
|
330
|
+
#
|
331
|
+
# Keys
|
332
|
+
#
|
251
333
|
|
252
334
|
# Delete a key
|
253
335
|
# @see http://redis.io/commands/del
|
@@ -457,7 +539,81 @@ module Oxblood
|
|
457
539
|
run(:TYPE, key)
|
458
540
|
end
|
459
541
|
|
460
|
-
#
|
542
|
+
#
|
543
|
+
# Lists
|
544
|
+
#
|
545
|
+
|
546
|
+
# Get the length of a list
|
547
|
+
# @see http://redis.io/commands/llen
|
548
|
+
#
|
549
|
+
# @param [String] key
|
550
|
+
#
|
551
|
+
# @return [Integer] the length of the list at key
|
552
|
+
# @return [RError] if the value stored at key is not a list
|
553
|
+
def llen(key)
|
554
|
+
run(:LLEN, key)
|
555
|
+
end
|
556
|
+
|
557
|
+
# Remove and get the first element in a list
|
558
|
+
# @see http://redis.io/commands/lpop
|
559
|
+
#
|
560
|
+
# @param [String] key
|
561
|
+
#
|
562
|
+
# @return [String, nil] the value of the first element,
|
563
|
+
# or nil when key does not exist.
|
564
|
+
def lpop(key)
|
565
|
+
run(:LPOP, key)
|
566
|
+
end
|
567
|
+
|
568
|
+
# Prepend one or multiple values to a list
|
569
|
+
# @see http://redis.io/commands/lpush
|
570
|
+
#
|
571
|
+
# @param [String] key
|
572
|
+
# @param [Array] values to prepend
|
573
|
+
#
|
574
|
+
# @return [Integer] the length of the list after the push operations
|
575
|
+
def lpush(key, *values)
|
576
|
+
run(*values.unshift(:LPUSH, key))
|
577
|
+
end
|
578
|
+
|
579
|
+
# Get a range of elements from a list
|
580
|
+
# @see http://redis.io/commands/lrange
|
581
|
+
#
|
582
|
+
# @param [String] key
|
583
|
+
# @param [Integer] start index
|
584
|
+
# @param [Integer] stop index
|
585
|
+
#
|
586
|
+
# @return [Array] list of elements in the specified range
|
587
|
+
def lrange(key, start, stop)
|
588
|
+
run(:LRANGE, key, start, stop)
|
589
|
+
end
|
590
|
+
|
591
|
+
# Remove and get the last element in a list
|
592
|
+
# @see http://redis.io/commands/rpop
|
593
|
+
#
|
594
|
+
# @param [String] key
|
595
|
+
#
|
596
|
+
# @return [String, nil] the value of the last element, or nil when key does
|
597
|
+
# not exist
|
598
|
+
def rpop(key)
|
599
|
+
run(:RPOP, key)
|
600
|
+
end
|
601
|
+
|
602
|
+
# Append one or multiple values to a list
|
603
|
+
# @see http://redis.io/commands/rpush
|
604
|
+
#
|
605
|
+
# @param [String] key
|
606
|
+
# @param [Array] values to add
|
607
|
+
#
|
608
|
+
# @return [Integer] the length of the list after the push operation
|
609
|
+
# @return [RError] if key holds a value that is not a list
|
610
|
+
def rpush(key, *values)
|
611
|
+
run(*values.unshift(:RPUSH, key))
|
612
|
+
end
|
613
|
+
|
614
|
+
#
|
615
|
+
# Sets
|
616
|
+
#
|
461
617
|
|
462
618
|
# Add one or more members to a set
|
463
619
|
# @see http://redis.io/commands/sadd
|
@@ -471,6 +627,39 @@ module Oxblood
|
|
471
627
|
run(*members.unshift(:SADD, key))
|
472
628
|
end
|
473
629
|
|
630
|
+
# Get the number of members in a set
|
631
|
+
# @see http://redis.io/commands/scard
|
632
|
+
#
|
633
|
+
# @param [String] key
|
634
|
+
#
|
635
|
+
# @return [Integer] the cardinality (number of elements) of the set, or 0 if
|
636
|
+
# key does not exist
|
637
|
+
def scard(key)
|
638
|
+
run(:SCARD, key)
|
639
|
+
end
|
640
|
+
|
641
|
+
# Get all the members in a set
|
642
|
+
# @see http://redis.io/commands/smembers
|
643
|
+
#
|
644
|
+
# @param [String] key
|
645
|
+
#
|
646
|
+
# @return [Array] all elements of the set
|
647
|
+
def smembers(key)
|
648
|
+
run(:SMEMBERS, key)
|
649
|
+
end
|
650
|
+
|
651
|
+
# Remove one or more members from a set
|
652
|
+
# @see http://redis.io/commands/srem
|
653
|
+
#
|
654
|
+
# @param [String] key
|
655
|
+
# @param [Array] members to remove
|
656
|
+
#
|
657
|
+
# @return [Integer] the number of members that were removed from the set,
|
658
|
+
# not including non existing members
|
659
|
+
def srem(key, *members)
|
660
|
+
run(*members.unshift(:SREM, key))
|
661
|
+
end
|
662
|
+
|
474
663
|
# Add multiple sets
|
475
664
|
# @see http://redis.io/commands/sunion
|
476
665
|
#
|
@@ -481,7 +670,9 @@ module Oxblood
|
|
481
670
|
run(*keys.unshift(:SUNION))
|
482
671
|
end
|
483
672
|
|
484
|
-
#
|
673
|
+
#
|
674
|
+
# Sorted Sets
|
675
|
+
#
|
485
676
|
|
486
677
|
# Add one or more members to a sorted set, or update its score if it already
|
487
678
|
# exists.
|
@@ -492,18 +683,59 @@ module Oxblood
|
|
492
683
|
#
|
493
684
|
# @param [String] key under which store set
|
494
685
|
# @param [[Float, String], Array<[Float, String]>] args scores and members
|
686
|
+
#
|
687
|
+
# @return [Integer] The number of elements added to the sorted sets, not
|
688
|
+
# including elements already existing for which the score was updated
|
495
689
|
def zadd(key, *args)
|
496
690
|
run(*args.unshift(:ZADD, key))
|
497
691
|
end
|
498
692
|
|
693
|
+
# Get the number of members in a sorted set
|
694
|
+
# @see http://redis.io/commands/zcard
|
695
|
+
#
|
696
|
+
# @param [String] key
|
697
|
+
#
|
698
|
+
# @return [Integer] the cardinality (number of elements) of the sorted set,
|
699
|
+
# or 0 if key does not exists
|
700
|
+
def zcard(key)
|
701
|
+
run(:ZCARD, key)
|
702
|
+
end
|
703
|
+
|
704
|
+
# Return a range of members in a sorted set, by index
|
705
|
+
# @see http://redis.io/commands/zrange
|
706
|
+
#
|
707
|
+
# @example
|
708
|
+
# session.zrange('myzset', 0, -1)
|
709
|
+
# # => ['one', 'two']
|
710
|
+
#
|
711
|
+
# @example
|
712
|
+
# session.zrange('myzset', 0, -1, withscores: true)
|
713
|
+
# # => [['one', '1'], ['two', '2']]
|
714
|
+
#
|
715
|
+
# @param [String] key
|
716
|
+
# @param [Integer] start index
|
717
|
+
# @param [Integer] stop index
|
718
|
+
# @param [Hash] opts
|
719
|
+
#
|
720
|
+
# @option opts [Boolean] :withscores (false) Return the scores of
|
721
|
+
# the elements together with the elements
|
722
|
+
#
|
723
|
+
# @return [Array] list of elements in the specified range (optionally with
|
724
|
+
# their scores, in case the :withscores option is given)
|
725
|
+
def zrange(key, start, stop, opts = {})
|
726
|
+
args = [:ZRANGE, key, start, stop]
|
727
|
+
args << :WITHSCORES if opts[:withscores]
|
728
|
+
run(*args)
|
729
|
+
end
|
730
|
+
|
499
731
|
# Return a range of members in a sorted set, by score
|
500
732
|
# @see http://redis.io/commands/zrangebyscore
|
501
733
|
#
|
502
734
|
# @todo Support optional args (WITHSCORES/LIMIT)
|
503
735
|
#
|
504
736
|
# @param [String] key under which set is stored
|
505
|
-
# @param [String] min
|
506
|
-
# @param [String] max
|
737
|
+
# @param [String] min score
|
738
|
+
# @param [String] max score
|
507
739
|
#
|
508
740
|
# @return [Array] list of elements in the specified score range
|
509
741
|
def zrangebyscore(key, min, max)
|
@@ -522,6 +754,18 @@ module Oxblood
|
|
522
754
|
run(*members.unshift(:ZREM, key))
|
523
755
|
end
|
524
756
|
|
757
|
+
# Remove all members in a sorted set within the given scores
|
758
|
+
# @see http://redis.io/commands/zremrangebyscore
|
759
|
+
#
|
760
|
+
# @param [String] key
|
761
|
+
# @param [String] min score
|
762
|
+
# @param [String] max score
|
763
|
+
#
|
764
|
+
# @return [Integer] the number of elements removed
|
765
|
+
def zremrangebyscore(key, min, max)
|
766
|
+
run(:ZREMRANGEBYSCORE, key, min, max)
|
767
|
+
end
|
768
|
+
|
525
769
|
protected
|
526
770
|
|
527
771
|
def serialize(*command)
|
@@ -529,8 +773,7 @@ module Oxblood
|
|
529
773
|
end
|
530
774
|
|
531
775
|
def run(*command)
|
532
|
-
@connection.
|
533
|
-
@connection.read_response
|
776
|
+
@connection.run_command(*command)
|
534
777
|
end
|
535
778
|
|
536
779
|
private
|
data/lib/oxblood/version.rb
CHANGED
@@ -10,7 +10,7 @@ class Redis
|
|
10
10
|
config = config.dup
|
11
11
|
config.delete(:path)
|
12
12
|
end
|
13
|
-
connection = ::Oxblood::Connection.
|
13
|
+
connection = ::Oxblood::Connection.new(config)
|
14
14
|
|
15
15
|
new(connection)
|
16
16
|
end
|
@@ -20,15 +20,15 @@ class Redis
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def connected?
|
23
|
-
@connection && @connection.connected?
|
23
|
+
@connection.socket && @connection.socket.connected?
|
24
24
|
end
|
25
25
|
|
26
26
|
def timeout=(timeout)
|
27
|
-
@connection.timeout = timeout > 0 ? timeout : nil
|
27
|
+
@connection.socket.timeout = timeout > 0 ? timeout : nil
|
28
28
|
end
|
29
29
|
|
30
30
|
def disconnect
|
31
|
-
@connection.close
|
31
|
+
@connection.socket.close
|
32
32
|
end
|
33
33
|
|
34
34
|
def write(command)
|
@@ -42,7 +42,7 @@ class Redis
|
|
42
42
|
reply
|
43
43
|
rescue ::Oxblood::Protocol::ParserError => e
|
44
44
|
raise Redis::ProtocolError.new(e.message)
|
45
|
-
rescue ::Oxblood::
|
45
|
+
rescue ::Oxblood::RSocket::TimeoutError => e
|
46
46
|
raise Redis::TimeoutError.new(e.message)
|
47
47
|
end
|
48
48
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oxblood
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.dev7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Shabanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- README.md
|
112
112
|
- Rakefile
|
113
113
|
- benchmarks/Gemfile
|
114
|
+
- benchmarks/pipeline.rb
|
114
115
|
- benchmarks/serializer.rb
|
115
116
|
- lib/oxblood.rb
|
116
117
|
- lib/oxblood/buffered_io.rb
|
@@ -118,6 +119,7 @@ files:
|
|
118
119
|
- lib/oxblood/pipeline.rb
|
119
120
|
- lib/oxblood/pool.rb
|
120
121
|
- lib/oxblood/protocol.rb
|
122
|
+
- lib/oxblood/rsocket.rb
|
121
123
|
- lib/oxblood/session.rb
|
122
124
|
- lib/oxblood/version.rb
|
123
125
|
- lib/redis/connection/oxblood.rb
|