async-redis 0.4.3 → 0.6.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
  SHA256:
3
- metadata.gz: 846a8f4d10913acf2d43343efb35a4ca9306db6a5fda6d9794774181939e6535
4
- data.tar.gz: 0d25f58941fc326a9500e50a63ead3ac017d734f8292e4ebcf63f1cad3ff6280
3
+ metadata.gz: 8a141b089c56a3e62a5e5f70d811127514105be73c208b1efec282e16427ba86
4
+ data.tar.gz: 28568c52ef21f29dcfdad24c2dd8f9460e558546de16e0599fc970b9d6581c1e
5
5
  SHA512:
6
- metadata.gz: d8881dfb830d61c74b55415d693f2eda5a5caf04daf789400df7d8e7229468c929300264d305b5182a706b44cca1677ae930ea757ca139dcfe2b7b639f0bc6c0
7
- data.tar.gz: b51195073ead610e2b95e67b9280c1b13472d51156985624782de72e9b3e57764ba2ad53b5758cedd9617e3ffc11a0668f89d6ee476e118f3300a600051c5973
6
+ metadata.gz: 69a6e6fb59f64444356f82a89ed3525e86ae0f465568265e4ff5b7103cefb0ff4433d41b3c7c8d325d3911649dc451dfe8fd82315ac94c4de47158d19da402de
7
+ data.tar.gz: 4ec4f9d4744508643cd01f9fcc4d4a937a74daca6ea7e61353e73d73e408490f8b501c0ee6434cf4127b1350eae279f1d49005515c6f9f86e940307ad757e3dc
data/lib/async/redis.rb CHANGED
@@ -22,3 +22,4 @@
22
22
 
23
23
  require_relative 'redis/version'
24
24
  require_relative 'redis/client'
25
+ require_relative 'redis/sentinels'
@@ -37,14 +37,14 @@ module Async
37
37
  # Legacy.
38
38
  ServerError = ::Protocol::Redis::ServerError
39
39
 
40
- def self.local_endpoint
41
- Async::IO::Endpoint.tcp('localhost', 6379)
40
+ def self.local_endpoint(port: 6379)
41
+ Async::IO::Endpoint.tcp('localhost', port)
42
42
  end
43
43
 
44
44
  class Client
45
45
  include ::Protocol::Redis::Methods
46
46
 
47
- def initialize(endpoint = Redis.local_endpoint, protocol = Protocol::RESP2, **options)
47
+ def initialize(endpoint = Redis.local_endpoint, protocol: Protocol::RESP2, **options)
48
48
  @endpoint = endpoint
49
49
  @protocol = protocol
50
50
 
@@ -74,10 +74,6 @@ module Async
74
74
  @pool.close
75
75
  end
76
76
 
77
- def publish(channel, message)
78
- call('PUBLISH', channel, message)
79
- end
80
-
81
77
  def subscribe(*channels)
82
78
  context = Context::Subscribe.new(@pool, channels)
83
79
 
@@ -90,18 +86,6 @@ module Async
90
86
  end
91
87
  end
92
88
 
93
- def multi(&block)
94
- context = Context::Multi.new(@pool)
95
-
96
- return context unless block_given?
97
-
98
- begin
99
- yield context
100
- ensure
101
- context.close
102
- end
103
- end
104
-
105
89
  def transaction(&block)
106
90
  context = Context::Transaction.new(@pool)
107
91
 
@@ -113,12 +97,14 @@ module Async
113
97
  context.close
114
98
  end
115
99
  end
116
-
100
+
101
+ alias multi transaction
102
+
117
103
  def pipeline(&block)
118
104
  context = Context::Pipeline.new(@pool)
119
-
105
+
120
106
  return context unless block_given?
121
-
107
+
122
108
  begin
123
109
  yield context
124
110
  ensure
@@ -145,7 +131,8 @@ module Async
145
131
  Async::Pool::Controller.wrap(**options) do
146
132
  peer = @endpoint.connect
147
133
 
148
- peer.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
134
+ # We will manage flushing ourselves:
135
+ peer.sync = true
149
136
 
150
137
  stream = IO::Stream.new(peer)
151
138
 
@@ -27,48 +27,35 @@ module Async
27
27
  module Redis
28
28
  module Context
29
29
  class Subscribe < Generic
30
+ MESSAGE = 'message'
31
+
30
32
  def initialize(pool, channels)
31
33
  super(pool)
32
34
 
33
- @channels = channels
34
-
35
35
  subscribe(channels)
36
36
  end
37
37
 
38
- def listen
39
- return @connection.read_response
38
+ def close
39
+ # There is no way to reset subscription state. On Redis v6+ you can use RESET, but this is not supported in <= v6.
40
+ @connection&.close
41
+
42
+ super
40
43
  end
41
44
 
42
- private
45
+ def listen
46
+ while response = @connection.read_response
47
+ return response if response.first == MESSAGE
48
+ end
49
+ end
43
50
 
44
51
  def subscribe(channels)
45
52
  @connection.write_request ['SUBSCRIBE', *channels]
46
53
  @connection.flush
47
-
48
- response = nil
49
-
50
- channels.length.times do |i|
51
- response = @connection.read_response
52
- end
53
-
54
- return response
55
54
  end
56
55
 
57
- def unsubscribe(*channels)
58
- if channels.empty? # unsubscribe from everything if no specific channels are given
59
- @connection.write_request ['UNSUBSCRIBE']
60
-
61
- response = nil
62
-
63
- @channels.length.times do |i|
64
- response = @connection.read_response
65
- end
66
-
67
- return response
68
- else
69
- @channels.subtract(channels)
70
- return call 'UNSUBSCRIBE', *channels
71
- end
56
+ def unsubscribe(channels)
57
+ @connection.write_request ['UNSUBSCRIBE', *channels]
58
+ @connection.flush
72
59
  end
73
60
  end
74
61
  end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Async
4
+ module Redis
5
+ class SentinelsClient < Client
6
+ def initialize(master_name, sentinels, role = :master, protocol = Protocol::RESP2, **options)
7
+ @master_name = master_name
8
+ @sentinel_endpoints = sentinels.map do |sentinel|
9
+ Async::IO::Endpoint.tcp(sentinel[:host], sentinel[:port])
10
+ end
11
+ @role = role
12
+
13
+ @protocol = protocol
14
+ @pool = connect(**options)
15
+ end
16
+
17
+ private
18
+
19
+ # Override the parent method. The only difference is that this one needs
20
+ # to resolve the master/slave address.
21
+ def connect(**options)
22
+ Async::Pool::Controller.wrap(**options) do
23
+ endpoint = resolve_address
24
+ peer = endpoint.connect
25
+ stream = IO::Stream.new(peer)
26
+
27
+ @protocol.client(stream)
28
+ end
29
+ end
30
+
31
+ def resolve_address
32
+ address = case @role
33
+ when :master then resolve_master
34
+ when :slave then resolve_slave
35
+ else raise ArgumentError, "Unknown instance role #{@role}"
36
+ end
37
+
38
+ address or raise RuntimeError, "Unable to fetch #{@role} via Sentinel."
39
+ end
40
+
41
+ def resolve_master
42
+ @sentinel_endpoints.each do |sentinel_endpoint|
43
+ client = Client.new(sentinel_endpoint)
44
+
45
+ begin
46
+ address = client.call('sentinel', 'get-master-addr-by-name', @master_name)
47
+ rescue Errno::ECONNREFUSED
48
+ next
49
+ end
50
+
51
+ return Async::IO::Endpoint.tcp(address[0], address[1]) if address
52
+ end
53
+
54
+ nil
55
+ end
56
+
57
+ def resolve_slave
58
+ @sentinel_endpoints.each do |sentinel_endpoint|
59
+ client = Client.new(sentinel_endpoint)
60
+
61
+ begin
62
+ reply = client.call('sentinel', 'slaves', @master_name)
63
+ rescue Errno::ECONNREFUSED
64
+ next
65
+ end
66
+
67
+ slaves = available_slaves(reply)
68
+ next if slaves.empty?
69
+
70
+ slave = select_slave(slaves)
71
+ return Async::IO::Endpoint.tcp(slave['ip'], slave['port'])
72
+ end
73
+
74
+ nil
75
+ end
76
+
77
+ def available_slaves(slaves_cmd_reply)
78
+ # The reply is an array with the format: [field1, value1, field2,
79
+ # value2, etc.].
80
+ # When a slave is marked as down by the sentinel, the "flags" field
81
+ # (comma-separated array) contains the "s_down" value.
82
+ slaves_cmd_reply.map { |s| s.each_slice(2).to_h }
83
+ .reject { |s| s.fetch('flags').split(',').include?('s_down') }
84
+ end
85
+
86
+ def select_slave(available_slaves)
87
+ available_slaves.sample
88
+ end
89
+ end
90
+ end
91
+ end
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Async
24
24
  module Redis
25
- VERSION = "0.4.3"
25
+ VERSION = "0.6.0"
26
26
  end
27
27
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  - Huba Nagy
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-02-11 00:00:00.000000000 Z
12
+ date: 2021-05-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: async
@@ -59,14 +59,14 @@ dependencies:
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: 0.4.0
62
+ version: 0.6.0
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: 0.4.0
69
+ version: 0.6.0
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: async-rspec
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -82,7 +82,7 @@ dependencies:
82
82
  - !ruby/object:Gem::Version
83
83
  version: '1.1'
84
84
  - !ruby/object:Gem::Dependency
85
- name: redis
85
+ name: benchmark-ips
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - ">="
@@ -96,7 +96,7 @@ dependencies:
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  - !ruby/object:Gem::Dependency
99
- name: benchmark-ips
99
+ name: bundler
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
102
  - - ">="
@@ -124,7 +124,7 @@ dependencies:
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  - !ruby/object:Gem::Dependency
127
- name: bundler
127
+ name: hiredis
128
128
  requirement: !ruby/object:Gem::Requirement
129
129
  requirements:
130
130
  - - ">="
@@ -138,21 +138,21 @@ dependencies:
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
140
  - !ruby/object:Gem::Dependency
141
- name: rspec
141
+ name: rake
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
- - - "~>"
144
+ - - ">="
145
145
  - !ruby/object:Gem::Version
146
- version: '3.6'
146
+ version: '0'
147
147
  type: :development
148
148
  prerelease: false
149
149
  version_requirements: !ruby/object:Gem::Requirement
150
150
  requirements:
151
- - - "~>"
151
+ - - ">="
152
152
  - !ruby/object:Gem::Version
153
- version: '3.6'
153
+ version: '0'
154
154
  - !ruby/object:Gem::Dependency
155
- name: rake
155
+ name: redis
156
156
  requirement: !ruby/object:Gem::Requirement
157
157
  requirements:
158
158
  - - ">="
@@ -166,35 +166,25 @@ dependencies:
166
166
  - !ruby/object:Gem::Version
167
167
  version: '0'
168
168
  - !ruby/object:Gem::Dependency
169
- name: hiredis
169
+ name: rspec
170
170
  requirement: !ruby/object:Gem::Requirement
171
171
  requirements:
172
- - - ">="
172
+ - - "~>"
173
173
  - !ruby/object:Gem::Version
174
- version: '0'
174
+ version: '3.6'
175
175
  type: :development
176
176
  prerelease: false
177
177
  version_requirements: !ruby/object:Gem::Requirement
178
178
  requirements:
179
- - - ">="
179
+ - - "~>"
180
180
  - !ruby/object:Gem::Version
181
- version: '0'
182
- description:
181
+ version: '3.6'
182
+ description:
183
183
  email:
184
- - samuel.williams@oriontransfer.co.nz
185
- - 12huba@gmail.com
186
184
  executables: []
187
185
  extensions: []
188
186
  extra_rdoc_files: []
189
187
  files:
190
- - ".editorconfig"
191
- - ".gitignore"
192
- - ".rspec"
193
- - ".travis.yml"
194
- - Gemfile
195
- - README.md
196
- - Rakefile
197
- - async-redis.gemspec
198
188
  - lib/async/redis.rb
199
189
  - lib/async/redis/client.rb
200
190
  - lib/async/redis/context/generic.rb
@@ -203,11 +193,13 @@ files:
203
193
  - lib/async/redis/context/transaction.rb
204
194
  - lib/async/redis/key.rb
205
195
  - lib/async/redis/protocol/resp2.rb
196
+ - lib/async/redis/sentinels.rb
206
197
  - lib/async/redis/version.rb
207
198
  homepage: https://github.com/socketry/async-redis
208
- licenses: []
199
+ licenses:
200
+ - MIT
209
201
  metadata: {}
210
- post_install_message:
202
+ post_install_message:
211
203
  rdoc_options: []
212
204
  require_paths:
213
205
  - lib
@@ -222,8 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
222
214
  - !ruby/object:Gem::Version
223
215
  version: '0'
224
216
  requirements: []
225
- rubygems_version: 3.0.6
226
- signing_key:
217
+ rubygems_version: 3.3.0.dev
218
+ signing_key:
227
219
  specification_version: 4
228
220
  summary: A Redis client library.
229
221
  test_files: []
data/.editorconfig DELETED
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
-
11
- # rspec failure tracking
12
- .rspec_status
13
- .covered.db
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper
data/.travis.yml DELETED
@@ -1,23 +0,0 @@
1
- language: ruby
2
- dist: xenial
3
- cache: bundler
4
-
5
- services:
6
- - redis-server
7
-
8
- matrix:
9
- include:
10
- - rvm: 2.3
11
- - rvm: 2.4
12
- - rvm: 2.5
13
- - rvm: 2.6
14
- - rvm: 2.6
15
- env: COVERAGE=BriefSummary,Coveralls
16
- - rvm: ruby-head
17
- - rvm: truffleruby
18
- - rvm: jruby-head
19
- env: JRUBY_OPTS="--debug -X+O"
20
- allow_failures:
21
- - rvm: ruby-head
22
- - rvm: truffleruby
23
- - rvm: jruby-head
data/Gemfile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in async-io.gemspec
6
- gemspec
7
-
8
- # gem "protocol-redis", path: "../protocol-redis"
9
-
10
- group :development do
11
- gem 'pry'
12
- end
data/README.md DELETED
@@ -1,125 +0,0 @@
1
- # Async::Redis
2
-
3
- An asynchronous client for Redis including TLS. Support for streaming requests and responses. Built on top of [async] and [async-io].
4
-
5
- [![Build Status](https://secure.travis-ci.org/socketry/async-redis.svg)](https://travis-ci.org/socketry/async-redis)
6
- [![Code Climate](https://codeclimate.com/github/socketry/async-redis.svg)](https://codeclimate.com/github/socketry/async-redis)
7
- [![Coverage Status](https://coveralls.io/repos/socketry/async-redis/badge.svg)](https://coveralls.io/r/socketry/async-redis)
8
-
9
- [async]: https://github.com/socketry/async
10
- [async-io]: https://github.com/socketry/async-io
11
-
12
- ## Installation
13
-
14
- Add this line to your application's Gemfile:
15
-
16
- ```ruby
17
- gem 'async-redis'
18
- ```
19
-
20
- And then execute:
21
-
22
- $ bundle
23
-
24
- Or install it yourself as:
25
-
26
- $ gem install async-redis
27
-
28
- ## Usage
29
-
30
- ### Basic Local Connection
31
-
32
- ```ruby
33
- require 'async/redis'
34
-
35
- endpoint = Async::Redis.local_endpoint
36
- client = Async::Redis::Client.new(endpoint)
37
-
38
- Async do
39
- pp client.info
40
- ensure
41
- client.close
42
- end
43
- ```
44
-
45
- ### Variables
46
-
47
- ```ruby
48
- require 'async'
49
- require 'async/redis'
50
-
51
- endpoint = Async::Redis.local_endpoint
52
- client = Async::Redis::Client.new(endpoint)
53
-
54
- Async do
55
- client.set('X', 10)
56
- pp client.get('X')
57
- ensure
58
- client.close
59
- end
60
- ```
61
-
62
- ### Subscriptions
63
-
64
- ```ruby
65
- require 'async'
66
- require 'async/redis'
67
-
68
- endpoint = Async::Redis.local_endpoint
69
- client = Async::Redis::Client.new(endpoint)
70
-
71
- Async do |task|
72
- condition = Async::Condition.new
73
-
74
- publisher = task.async do
75
- condition.wait
76
-
77
- client.publish 'status.frontend', 'good'
78
- end
79
-
80
- subscriber = task.async do
81
- client.subscribe 'status.frontend' do |context|
82
- condition.signal # We are waiting for messages.
83
-
84
- type, name, message = context.listen
85
-
86
- pp type, name, message
87
- end
88
- end
89
- ensure
90
- client.close
91
- end
92
- ```
93
-
94
- ## Contributing
95
-
96
- 1. Fork it
97
- 2. Create your feature branch (`git checkout -b my-new-feature`)
98
- 3. Commit your changes (`git commit -am 'Add some feature'`)
99
- 4. Push to the branch (`git push origin my-new-feature`)
100
- 5. Create new Pull Request
101
-
102
- ## License
103
-
104
- Released under the MIT license.
105
-
106
- Copyright, 2018, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
107
- Copyright, 2018, by Huba Z. Nagy.
108
-
109
- Permission is hereby granted, free of charge, to any person obtaining a copy
110
- of this software and associated documentation files (the "Software"), to deal
111
- in the Software without restriction, including without limitation the rights
112
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
113
- copies of the Software, and to permit persons to whom the Software is
114
- furnished to do so, subject to the following conditions:
115
-
116
- The above copyright notice and this permission notice shall be included in
117
- all copies or substantial portions of the Software.
118
-
119
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
120
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
121
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
122
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
123
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
124
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
125
- THE SOFTWARE.
data/Rakefile DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:test)
7
-
8
- task :default => :test
9
-
10
- task :client do
11
- require 'irb'
12
- require 'async/redis/client'
13
-
14
- endpoint = Async::Redis.local_endpoint
15
- client = Async::Redis::Client.new(endpoint)
16
-
17
- Async do
18
- binding.irb
19
- end
20
- end
data/async-redis.gemspec DELETED
@@ -1,39 +0,0 @@
1
-
2
- require_relative 'lib/async/redis/version'
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "async-redis"
6
- spec.version = Async::Redis::VERSION
7
- spec.authors = ["Samuel Williams", "Huba Nagy"]
8
- spec.email = ["samuel.williams@oriontransfer.co.nz", "12huba@gmail.com"]
9
-
10
- spec.summary = "A Redis client library."
11
- spec.homepage = "https://github.com/socketry/async-redis"
12
-
13
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
14
- f.match(%r{^(test|spec|features)/})
15
- end
16
-
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.require_paths = ["lib"]
19
-
20
- spec.add_dependency("async", "~> 1.8")
21
- spec.add_dependency("async-io", "~> 1.10")
22
- spec.add_dependency("async-pool", "~> 0.2")
23
-
24
- spec.add_dependency("protocol-redis", "~> 0.4.0")
25
-
26
- spec.add_development_dependency "async-rspec", "~> 1.1"
27
- spec.add_development_dependency "redis"
28
- spec.add_development_dependency "benchmark-ips"
29
-
30
- spec.add_development_dependency "covered"
31
- spec.add_development_dependency "bundler"
32
- spec.add_development_dependency "rspec", "~> 3.6"
33
- spec.add_development_dependency "rake"
34
-
35
- # Dependencies with C extensions
36
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby"
37
- spec.add_development_dependency "hiredis"
38
- end
39
- end