renoir 0.1.1 → 0.2.0

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: 461d2ccdb278c0d426ec55fdd18842ada523e45e
4
- data.tar.gz: 55e8b51738dbfee6aa2e9e679ac5fce0e2cded25
3
+ metadata.gz: 1c38bafe64d4e6e17eb9e8ac7bfda4518a16831f
4
+ data.tar.gz: 934d5766ccfa78fe3d7113f937ced20a2d9e1ae5
5
5
  SHA512:
6
- metadata.gz: b2d01e508710e5be5ae0b2c73abb2019686e3623731b7cb1dfa527fa745946f8164e2995ee5e0eade1a579afe89f7f049a1d31543fe48712641cbfa3b406b19e
7
- data.tar.gz: 20e8bf79891cf2f5631aa535426d0ccb3cc047d305bef1821be0f13a9fe6f68f830831511b40932590307d89265b3e9c55ea8339bf229346b90e3079ffff5b8c
6
+ metadata.gz: 29202c8ae95394db795908d472645f1134c04a50ead0833c6fd7c7348e3b251b4f9a9f2f12d0e91f621c041cd379430fdd0cb81e920f77467a103f629a6b73a5
7
+ data.tar.gz: 2521e02fd50177c9e2a380eb5025122177964d1d708ed4b2693873d417ec96771c27653a77271a326f4bae11b8aa9e956252dae49cce8c8c7ee0f113de89f07a
data/README.md CHANGED
@@ -46,10 +46,37 @@ rc = Renoir::Client.new(
46
46
 
47
47
  # redis-rb options
48
48
  timeout: 100,
49
- password: 'password'
49
+ password: 'password',
50
+ driver: :hiredis
50
51
  )
51
52
  ```
52
53
 
54
+ ### Pipelining
55
+
56
+ Command pipelining is supported although "future" variable is not available at this point.
57
+
58
+ ```ruby
59
+ rc.pipeliend do |pipeline|
60
+ pipeline.set('hoge{1}', 123)
61
+ pipeline.get('hoge{1}')
62
+ pipeline.get('fuga{1}')
63
+ end
64
+ # => ["OK", "123", nil]
65
+ ```
66
+
67
+ If pipelined commands use different slot of key, it fails without dispatching command.
68
+
69
+ You can also execute commands atomically with `#multi`:
70
+
71
+ ```ruby
72
+ rc.multi do |pipeline|
73
+ pipeline.set('hoge{1}', 123)
74
+ pipeline.get('hoge{1}')
75
+ pipeline.get('fuga{1}')
76
+ end
77
+ # => ["OK", "123", nil]
78
+ ```
79
+
53
80
  ### Dispatch command to nodes directly
54
81
 
55
82
  Renoir dispatches a command only if a slot is determined by the command. This also includes no-keys commands like `KEYS`, `BGSAVE` and so on.
data/lib/renoir/client.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'thread'
2
2
  require "renoir/cluster_info"
3
+ require "renoir/pipeline"
3
4
  require "renoir/connection_adapters"
4
5
  require "renoir/crc16"
5
6
 
@@ -50,23 +51,30 @@ module Renoir
50
51
  end
51
52
 
52
53
  def eval(*args, &block)
53
- call(eval, *args, &block)
54
+ call(:eval, *args, &block)
54
55
  end
55
56
 
56
- def call(*command, &block)
57
- keys = @adapter_class.get_keys_from_command(command)
58
- slots = keys.map { |key| key_slot(key) }.uniq
59
- fail "No way to dispatch this command to Redis Cluster." if slots.size != 1
60
- slot = slots.first
57
+ def multi(&block)
58
+ commands = pipeline_commands(&block)
59
+ slot = get_slot_from_commands(commands)
61
60
 
62
- refresh = @refresh_slots_mutex.synchronize do
63
- refresh = @refresh_slots
64
- @refresh_slots = false
65
- refresh
66
- end
67
- refresh_slots if refresh
61
+ refresh_slots
62
+ call_with_redirection(slot, [[:multi]] + commands + [[:exec]])
63
+ end
64
+
65
+ def pipelined(&block)
66
+ commands = pipeline_commands(&block)
67
+ slot = get_slot_from_commands(commands)
68
+
69
+ refresh_slots
70
+ call_with_redirection(slot, commands)
71
+ end
72
+
73
+ def call(*command, &block)
74
+ slot = get_slot_from_commands([command])
68
75
 
69
- call_with_redirection(slot, command, &block)
76
+ refresh_slots
77
+ call_with_redirection(slot, [command], &block)[0]
70
78
  end
71
79
 
72
80
  def close
@@ -102,7 +110,22 @@ module Renoir
102
110
  CRC16.crc16(key) % REDIS_CLUSTER_HASH_SLOTS
103
111
  end
104
112
 
105
- def call_with_redirection(slot, command, &block)
113
+ def get_slot_from_commands(commands)
114
+ keys = commands.flat_map { |command| @adapter_class.get_keys_from_command(command) }.uniq
115
+ slots = keys.map { |key| key_slot(key) }.uniq
116
+ fail "No way to dispatch this command to Redis Cluster." if slots.size != 1
117
+ slots.first
118
+ end
119
+
120
+ def pipeline_commands(&block)
121
+ pipeline = Pipeline.new(
122
+ connection_adapter: @options[:connection_adapter]
123
+ )
124
+ yield pipeline
125
+ pipeline.commands
126
+ end
127
+
128
+ def call_with_redirection(slot, commands, &block)
106
129
  nodes = @cluster_info.nodes.dup
107
130
  node = @cluster_info.slot_node(slot) || nodes.sample
108
131
 
@@ -114,7 +137,7 @@ module Renoir
114
137
  nodes.delete(node)
115
138
 
116
139
  conn = fetch_connection(node)
117
- reply = conn.call(command, asking, &block)
140
+ reply = conn.call(commands, asking, &block)
118
141
  case reply
119
142
  when ConnectionAdapters::Reply::RedirectionError
120
143
  asking = reply.ask
@@ -140,10 +163,17 @@ module Renoir
140
163
  end
141
164
 
142
165
  def refresh_slots
166
+ refresh = @refresh_slots_mutex.synchronize do
167
+ refresh = @refresh_slots
168
+ @refresh_slots = false
169
+ refresh
170
+ end
171
+ return unless refresh
172
+
143
173
  slots = nil
144
174
  @cluster_info.nodes.each do |node|
145
175
  conn = fetch_connection(node)
146
- reply = conn.call(["cluster", "slots"])
176
+ reply = conn.call([["cluster", "slots"]])
147
177
  case reply
148
178
  when ConnectionAdapters::Reply::RedirectionError
149
179
  fail "never reach here"
@@ -152,7 +182,7 @@ module Renoir
152
182
  @logger.warn("CLUSTER SLOTS command failed: node_name=#{node[:name]}, message=#{reply.cause}")
153
183
  end
154
184
  else
155
- slots = reply
185
+ slots = reply[0]
156
186
  break
157
187
  end
158
188
  end
@@ -7,7 +7,7 @@ module Renoir
7
7
  end
8
8
  end
9
9
 
10
- def call(command, asking=false, &block)
10
+ def call(commands, asking=false, &block)
11
11
  fail "a connection adapter must override #call"
12
12
  end
13
13
 
@@ -59,15 +59,30 @@ module Renoir
59
59
  @conn = ::Redis.new(options.merge(host: host, port: port))
60
60
  end
61
61
 
62
- def call(command, asking=false, &block)
63
- command, *args = command
64
- if asking
65
- @conn.multi do |tx|
66
- tx.asking
67
- tx.send(command, *args, &block)
62
+ def call(commands, asking=false, &block)
63
+ if commands[0][0].to_sym == :multi
64
+ fail 'EXEC command is required for MULTI' if commands[-1][0].to_sym != :exec
65
+ commands = commands[1..-2]
66
+ multi = true
67
+ end
68
+
69
+ if multi || asking
70
+ replies = @conn.multi do |tx|
71
+ tx.asking if asking
72
+ commands.each do |command, *args|
73
+ tx.send(command, *args, &block)
74
+ end
75
+ end
76
+ asking ? replies.slice(1..-1) : replies
77
+ elsif commands.size > 1
78
+ @conn.pipelined do |pipeline|
79
+ commands.each do |command, *args|
80
+ pipeline.send(command, *args, &block)
81
+ end
68
82
  end
69
83
  else
70
- @conn.send(command, *args, &block)
84
+ command, *args = commands[0]
85
+ [@conn.send(command, *args, &block)]
71
86
  end
72
87
  rescue ::Redis::CommandError => e
73
88
  errv = e.to_s.split
@@ -0,0 +1,21 @@
1
+ module Renoir
2
+ class Pipeline
3
+ attr_reader :commands
4
+
5
+ def initialize(options={})
6
+ @commands = []
7
+ end
8
+
9
+ def eval(*args)
10
+ call(:eval, *args)
11
+ end
12
+
13
+ def call(*command)
14
+ @commands << command
15
+ end
16
+
17
+ def method_missing(command, *args, &block)
18
+ call(command, *args, &block)
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Renoir
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: renoir
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroshi Saito
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-06 00:00:00.000000000 Z
11
+ date: 2017-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -90,6 +90,7 @@ files:
90
90
  - lib/renoir/connection_adapters/redis.rb
91
91
  - lib/renoir/connection_adapters/reply.rb
92
92
  - lib/renoir/crc16.rb
93
+ - lib/renoir/pipeline.rb
93
94
  - lib/renoir/version.rb
94
95
  - renoir.gemspec
95
96
  homepage: https://github.com/saidie/renoir
@@ -112,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
113
  version: '0'
113
114
  requirements: []
114
115
  rubyforge_project:
115
- rubygems_version: 2.5.2
116
+ rubygems_version: 2.5.1
116
117
  signing_key:
117
118
  specification_version: 4
118
119
  summary: Reliable Redis Cluster client library