oxblood 0.1.0.dev9 → 0.1.0.dev10
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 +1 -1
- data/benchmarks/Gemfile +2 -0
- data/benchmarks/pipeline.rb +23 -0
- data/benchmarks/pool.rb +45 -0
- data/lib/oxblood/commands/lists.rb +69 -0
- data/lib/oxblood/pipeline.rb +2 -0
- data/lib/oxblood/rsocket.rb +11 -10
- data/lib/oxblood/session.rb +2 -0
- data/lib/oxblood/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa40f0d5b2f3279151d1689abb30900a658e4b42
|
4
|
+
data.tar.gz: 1dab3c062674dc0cacae8ed38bd54b01965fc962
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cca554b76261801ee8805aef706002efcfbed3412cf93105f74885b329e80b3d2632b1e53b586199bf610c0f584f856d744fd8eede3b6ccc58ac83b25c355b8d
|
7
|
+
data.tar.gz: 26168ad1e57c524f137374865648680f4d684f0ea4ade6a71664cf9d7ee70507cfcfc5c9ab946e42bb91fd189ce7fd89218fbb2141453404d211a98a0785924b
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ An experimental Redis Ruby client.
|
|
22
22
|
- Hashes (14/15) (See [#3](https://github.com/etehtsea/oxblood/issues/3))
|
23
23
|
- HyperLogLog (0/3)
|
24
24
|
- Keys (18/22) (See [#4], [#6], [#7], [#8])
|
25
|
-
- Lists
|
25
|
+
- Lists
|
26
26
|
- Pub/Sub (0/6)
|
27
27
|
- Scripting (0/7)
|
28
28
|
- Server (2/31)
|
data/benchmarks/Gemfile
CHANGED
data/benchmarks/pipeline.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'hiredis' unless RUBY_ENGINE == 'jruby'
|
1
2
|
require 'redis'
|
2
3
|
require 'oxblood'
|
3
4
|
require 'benchmark'
|
@@ -19,6 +20,16 @@ def redis_with
|
|
19
20
|
r.pipelined { N.times { r.ping } }
|
20
21
|
end
|
21
22
|
|
23
|
+
def hiredis_without
|
24
|
+
r = Redis.new(driver: :hiredis)
|
25
|
+
N.times { r.ping }
|
26
|
+
end
|
27
|
+
|
28
|
+
def hiredis_with
|
29
|
+
r = Redis.new(driver: :hiredis)
|
30
|
+
r.pipelined { N.times { r.ping } }
|
31
|
+
end
|
32
|
+
|
22
33
|
def oxblood_without
|
23
34
|
r = Oxblood::Session.new(Oxblood::Connection.new)
|
24
35
|
N.times { r.ping }
|
@@ -30,7 +41,19 @@ def oxblood_with
|
|
30
41
|
pipe.sync
|
31
42
|
end
|
32
43
|
|
44
|
+
# Warmup JVM
|
45
|
+
if RUBY_ENGINE == 'jruby'
|
46
|
+
redis_without
|
47
|
+
redis_with
|
48
|
+
oxblood_without
|
49
|
+
oxblood_with
|
50
|
+
end
|
51
|
+
|
33
52
|
benchmark('redis without') { redis_without }
|
34
53
|
benchmark('redis with') { redis_with }
|
35
54
|
benchmark('oxblood without') { oxblood_without }
|
36
55
|
benchmark('oxblood with') { oxblood_with }
|
56
|
+
unless RUBY_ENGINE == 'jruby'
|
57
|
+
benchmark('hiredis without') { hiredis_without }
|
58
|
+
benchmark('hiredis with') { hiredis_with }
|
59
|
+
end
|
data/benchmarks/pool.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
require 'redis'
|
3
|
+
require 'oxblood'
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
N = 10_000
|
7
|
+
TASKS = 1_000
|
8
|
+
POOL_SIZE = 32
|
9
|
+
|
10
|
+
def worker_pool
|
11
|
+
Concurrent::FixedThreadPool.new(POOL_SIZE * 2)
|
12
|
+
end
|
13
|
+
|
14
|
+
RedisPool = ConnectionPool.new(size: POOL_SIZE) { Redis.new }
|
15
|
+
OxbloodPool = Oxblood::Pool.new(size: POOL_SIZE)
|
16
|
+
|
17
|
+
def benchmark(label, &blk)
|
18
|
+
sec = Benchmark.realtime(&blk)
|
19
|
+
puts [label, sec.round(3)].join(': ')
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(&blk)
|
23
|
+
pool = worker_pool
|
24
|
+
TASKS.times { pool.post(&blk) }
|
25
|
+
sleep 0.1 while pool.completed_task_count != TASKS
|
26
|
+
end
|
27
|
+
|
28
|
+
def redis
|
29
|
+
RedisPool.with { |r| r.pipelined { N.times { r.ping } } }
|
30
|
+
end
|
31
|
+
|
32
|
+
def oxblood
|
33
|
+
OxbloodPool.pipelined { |p| N.times { p.ping } }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Warmup JVM
|
37
|
+
if RUBY_ENGINE == 'jruby'
|
38
|
+
10.times do
|
39
|
+
redis
|
40
|
+
oxblood
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
benchmark('redis-rb') { run { redis } }
|
45
|
+
benchmark('oxblood') { run { oxblood } }
|
@@ -1,6 +1,49 @@
|
|
1
1
|
module Oxblood
|
2
2
|
module Commands
|
3
3
|
module Lists
|
4
|
+
# Remove and get the first element in a list, or block until one
|
5
|
+
# is available
|
6
|
+
# @see http://redis.io/commands/blpop
|
7
|
+
#
|
8
|
+
# @param [String, Array<String>] keys
|
9
|
+
# @param [Integer] timeout in seconds
|
10
|
+
#
|
11
|
+
# @return [nil] when no element could be popped and the timeout expired
|
12
|
+
# @return [[String, String]] a two-element multi-bulk with the first
|
13
|
+
# element being the name of the key where an element was popped and
|
14
|
+
# the second element being the value of the popped element
|
15
|
+
def blpop(*keys, timeout)
|
16
|
+
blocking_pop(:BLPOP, keys, timeout)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Remove and get the last element in a list, or block until one
|
20
|
+
# is available
|
21
|
+
# @see http://redis.io/commands/brpop
|
22
|
+
#
|
23
|
+
# @param [String, Array<String>] keys
|
24
|
+
# @param [Integer] timeout in seconds
|
25
|
+
#
|
26
|
+
# @return [nil] when no element could be popped and the timeout expired
|
27
|
+
# @return [[String, String]] a two-element multi-bulk with the first
|
28
|
+
# element being the name of the key where an element was popped and
|
29
|
+
# the second element being the value of the popped element
|
30
|
+
def brpop(*keys, timeout)
|
31
|
+
blocking_pop(:BRPOP, keys, timeout)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Pop a value from a list, push it to another list and return it;
|
35
|
+
# or block until one is available
|
36
|
+
# @see http://redis.io/commands/brpoplpush
|
37
|
+
#
|
38
|
+
# @param [String] source
|
39
|
+
# @param [String] destination
|
40
|
+
#
|
41
|
+
# @return [nil] when no element could be popped and the timeout expired
|
42
|
+
# @return [String] the element being popped and pushed
|
43
|
+
def brpoplpush(source, destination, timeout)
|
44
|
+
blocking_pop(:BRPOPLPUSH, [source, destination], timeout)
|
45
|
+
end
|
46
|
+
|
4
47
|
# Get an element from a list by its index
|
5
48
|
# @see http://www.redis.io/commands/lindex
|
6
49
|
#
|
@@ -164,6 +207,32 @@ module Oxblood
|
|
164
207
|
def rpushx(key, value)
|
165
208
|
run(:RPUSHX, key, value)
|
166
209
|
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
# @note Mutates keys argument!
|
214
|
+
def blocking_pop(command, keys, timeout)
|
215
|
+
with_custom_timeout(timeout) do
|
216
|
+
run(*keys.unshift(command).push(timeout))
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Temporary increase socket timeout for blocking operations
|
221
|
+
# @note non-threadsafe!
|
222
|
+
def with_custom_timeout(timeout)
|
223
|
+
old_timeout = connection.socket.timeout
|
224
|
+
|
225
|
+
if timeout.zero?
|
226
|
+
# Indefinite blocking means 0 on redis server and nil on ruby
|
227
|
+
connection.socket.timeout = nil
|
228
|
+
else
|
229
|
+
connection.socket.timeout += timeout
|
230
|
+
end
|
231
|
+
|
232
|
+
yield
|
233
|
+
ensure
|
234
|
+
connection.socket.timeout = old_timeout
|
235
|
+
end
|
167
236
|
end
|
168
237
|
end
|
169
238
|
end
|
data/lib/oxblood/pipeline.rb
CHANGED
data/lib/oxblood/rsocket.rb
CHANGED
@@ -8,9 +8,10 @@ module Oxblood
|
|
8
8
|
class RSocket
|
9
9
|
TimeoutError = Class.new(RuntimeError)
|
10
10
|
|
11
|
-
# JRuby don't properly support SO_LINGER setting
|
11
|
+
# JRuby before 9.1.6.0 don't properly support SO_LINGER setting
|
12
12
|
# @see https://github.com/jruby/jruby/issues/4040
|
13
|
-
LINGER_OPTION = if RUBY_ENGINE == 'jruby'
|
13
|
+
LINGER_OPTION = if RUBY_ENGINE == 'jruby' &&
|
14
|
+
Gem::Version.new(JRUBY_VERSION) < Gem::Version.new('9.1.6.0')
|
14
15
|
[Socket::SOL_SOCKET, :LINGER, 0].freeze
|
15
16
|
else
|
16
17
|
Socket::Option.linger(true, 0)
|
@@ -42,11 +43,11 @@ module Oxblood
|
|
42
43
|
# Read number of bytes
|
43
44
|
# @param [Integer] nbytes number of bytes to read
|
44
45
|
# @return [String] read result
|
45
|
-
def read(nbytes)
|
46
|
+
def read(nbytes, timeout = @timeout)
|
46
47
|
result = @buffer.slice!(0, nbytes)
|
47
48
|
|
48
49
|
while result.bytesize < nbytes
|
49
|
-
result << readpartial(nbytes - result.bytesize)
|
50
|
+
result << readpartial(nbytes - result.bytesize, timeout)
|
50
51
|
end
|
51
52
|
|
52
53
|
result
|
@@ -55,9 +56,9 @@ module Oxblood
|
|
55
56
|
# Read until separator
|
56
57
|
# @param [String] separator separator
|
57
58
|
# @return [String] read result
|
58
|
-
def gets(separator)
|
59
|
+
def gets(separator, timeout = @timeout)
|
59
60
|
while (crlf = @buffer.index(separator)).nil?
|
60
|
-
@buffer << readpartial(1024)
|
61
|
+
@buffer << readpartial(1024, timeout)
|
61
62
|
end
|
62
63
|
|
63
64
|
@buffer.slice!(0, crlf + separator.bytesize)
|
@@ -66,14 +67,14 @@ module Oxblood
|
|
66
67
|
# Write data to socket
|
67
68
|
# @param [String] data given
|
68
69
|
# @return [Integer] the number of bytes written
|
69
|
-
def write(data)
|
70
|
+
def write(data, timeout = @timeout)
|
70
71
|
full_size = data.bytesize
|
71
72
|
|
72
73
|
while data.bytesize > 0
|
73
74
|
written = socket.write_nonblock(data, exception: false)
|
74
75
|
|
75
76
|
if written == :wait_writable
|
76
|
-
socket.wait_writable(
|
77
|
+
socket.wait_writable(timeout) or fail_with_timeout!
|
77
78
|
else
|
78
79
|
data = data.byteslice(written..-1)
|
79
80
|
end
|
@@ -119,12 +120,12 @@ module Oxblood
|
|
119
120
|
end
|
120
121
|
end
|
121
122
|
|
122
|
-
def readpartial(nbytes)
|
123
|
+
def readpartial(nbytes, timeout)
|
123
124
|
case data = socket.read_nonblock(nbytes, exception: false)
|
124
125
|
when String
|
125
126
|
return data
|
126
127
|
when :wait_readable
|
127
|
-
socket.wait_readable(
|
128
|
+
socket.wait_readable(timeout) or fail_with_timeout!
|
128
129
|
when nil
|
129
130
|
close
|
130
131
|
raise Errno::ECONNRESET
|
data/lib/oxblood/session.rb
CHANGED
data/lib/oxblood/version.rb
CHANGED
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.dev10
|
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-
|
11
|
+
date: 2016-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: connection_pool
|
@@ -112,6 +112,7 @@ files:
|
|
112
112
|
- Rakefile
|
113
113
|
- benchmarks/Gemfile
|
114
114
|
- benchmarks/pipeline.rb
|
115
|
+
- benchmarks/pool.rb
|
115
116
|
- benchmarks/serializer.rb
|
116
117
|
- lib/oxblood.rb
|
117
118
|
- lib/oxblood/commands.rb
|
@@ -154,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
155
|
version: 1.3.1
|
155
156
|
requirements: []
|
156
157
|
rubyforge_project:
|
157
|
-
rubygems_version: 2.6.
|
158
|
+
rubygems_version: 2.6.7
|
158
159
|
signing_key:
|
159
160
|
specification_version: 4
|
160
161
|
summary: A Ruby Redis client
|