oxblood 0.1.0.dev9 → 0.1.0.dev10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef39e93b7bf0f2dc011433a59874028fcc1e49e6
4
- data.tar.gz: f9c730aac3b62cc6e4cfb3d76bd0fb29d87bcc76
3
+ metadata.gz: aa40f0d5b2f3279151d1689abb30900a658e4b42
4
+ data.tar.gz: 1dab3c062674dc0cacae8ed38bd54b01965fc962
5
5
  SHA512:
6
- metadata.gz: e22b6aea29ce947a9f735ab788a29e14d1ada26cdb411cd3970e1b703fa62b3b14d59b83829edecc46f1eec3539b8538313fb3182758096adf7d0d07adbcd7a9
7
- data.tar.gz: b32802b13c4569f8b7cc164b2a2ee5d0a0932b9320788626e95bc025e557b828b3046798d49a55b302fc1e271c4796dd900358c6cf9213349bb73f2f0f6912c1
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 (14/17) (See [#11](https://github.com/etehtsea/oxblood/issues/11))
25
+ - Lists
26
26
  - Pub/Sub (0/6)
27
27
  - Scripting (0/7)
28
28
  - Server (2/31)
data/benchmarks/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'hiredis', platforms: :mri
3
4
  gem 'oxblood', path: '..'
4
5
  gem 'benchmark-ips'
5
6
  gem 'redis'
7
+ gem 'concurrent-ruby'
@@ -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
@@ -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
@@ -16,6 +16,8 @@ module Oxblood
16
16
  class Pipeline
17
17
  include Oxblood::Commands
18
18
 
19
+ attr_reader :connection
20
+
19
21
  def initialize(connection)
20
22
  @connection = connection
21
23
  @commands = Array.new
@@ -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(@timeout) or fail_with_timeout!
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(@timeout) or fail_with_timeout!
128
+ socket.wait_readable(timeout) or fail_with_timeout!
128
129
  when nil
129
130
  close
130
131
  raise Errno::ECONNRESET
@@ -15,6 +15,8 @@ module Oxblood
15
15
  class Session
16
16
  include Oxblood::Commands
17
17
 
18
+ attr_reader :connection
19
+
18
20
  def initialize(connection)
19
21
  @connection = connection
20
22
  end
@@ -1,3 +1,3 @@
1
1
  module Oxblood
2
- VERSION = '0.1.0.dev9'
2
+ VERSION = '0.1.0.dev10'
3
3
  end
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.dev9
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-09-20 00:00:00.000000000 Z
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.6
158
+ rubygems_version: 2.6.7
158
159
  signing_key:
159
160
  specification_version: 4
160
161
  summary: A Ruby Redis client