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 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