oxblood 0.1.0.dev6 → 0.1.0.dev7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://badge.fury.io/rb/oxblood)
|
4
4
|
[](http://rubydoc.info/github/etehtsea/oxblood/master/frames)
|
5
|
+
[](https://travis-ci.org/etehtsea/oxblood)
|
5
6
|
[](https://codeclimate.com/github/etehtsea/oxblood)
|
6
7
|
[](https://codeclimate.com/github/etehtsea/oxblood/coverage)
|
7
|
-
[](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
|
-
[](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
|