renoir 0.1.1 → 0.2.0

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