async-redis 0.5.0 → 0.5.1

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
  SHA256:
3
- metadata.gz: d6d0663e06a759b527cd94e0098f963e74c6c5c24dfc0f36f77a03bc5c719776
4
- data.tar.gz: 88530a16ce78f5a55399010f287be6008350adabc7f911e39560e45f3d11a595
3
+ metadata.gz: d30c4c6db1611c8d2d65ea9ba1c807b2aecc1522ac9e936ff969db8c1aa3196d
4
+ data.tar.gz: a1acc4eabeff0c0f5f0f1a6b90353605d1c6452e43bf4da15b7c108e5de21a81
5
5
  SHA512:
6
- metadata.gz: f8bc38e694669b220071a3b50f555e6e485d4eb66656be2ab8d8680615d01f46501ae92a00081b75e8206913b013d1985c61716de0a40af105da882cc3b54500
7
- data.tar.gz: 509b6e77d40be0858035cf2a1e53667830f4c106d0f7235aee8a1c7a2493d6e87622a06fb0c6f420088366e196ef0d85bb758aa66b56b79003aac3895f06c120
6
+ metadata.gz: 3741bb0209230896d293dd109c86315dd154a23c488ef278ad611b8a26f6ac5e055cecbd7930d274a878507ac4c7cd722080baefb3d83425cc655beabc34ba51
7
+ data.tar.gz: 609fb8b39c3a7bf430a1aac0de4d9234f44d6f63fea22b2ecd61d57763371b8be3374a975100658b1f8bdb891fd37f3c7c0a5390769d15733648d4fa3797b596
@@ -22,3 +22,4 @@
22
22
 
23
23
  require_relative 'redis/version'
24
24
  require_relative 'redis/client'
25
+ require_relative 'redis/sentinels'
@@ -37,8 +37,8 @@ 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
@@ -101,13 +101,14 @@ module Async
101
101
  context.close
102
102
  end
103
103
  end
104
+
104
105
  alias multi transaction
105
-
106
+
106
107
  def pipeline(&block)
107
108
  context = Context::Pipeline.new(@pool)
108
-
109
+
109
110
  return context unless block_given?
110
-
111
+
111
112
  begin
112
113
  yield context
113
114
  ensure
@@ -35,6 +35,13 @@ module Async
35
35
  subscribe(channels)
36
36
  end
37
37
 
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
43
+ end
44
+
38
45
  def listen
39
46
  return @connection.read_response
40
47
  end
@@ -67,7 +74,7 @@ module Async
67
74
  return response
68
75
  else
69
76
  @channels.subtract(channels)
70
- return call 'UNSUBSCRIBE', *channels
77
+ return call('UNSUBSCRIBE', *channels)
71
78
  end
72
79
  end
73
80
  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.5.0"
25
+ VERSION = "0.5.1"
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.5.0
4
+ version: 0.5.1
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-22 00:00:00.000000000 Z
12
+ date: 2020-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: async
@@ -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
- - ".github/workflows/development.yml"
192
- - ".gitignore"
193
- - ".rspec"
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
@@ -223,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
215
  version: '0'
224
216
  requirements: []
225
217
  rubygems_version: 3.1.2
226
- signing_key:
218
+ signing_key:
227
219
  specification_version: 4
228
220
  summary: A Redis client library.
229
221
  test_files: []
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
@@ -1,36 +0,0 @@
1
- name: Development
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- test:
7
- strategy:
8
- matrix:
9
- os:
10
- - ubuntu
11
- - macos
12
-
13
- ruby:
14
- - 2.4
15
- - 2.5
16
- - 2.6
17
- - 2.7
18
-
19
- include:
20
- - os: 'ubuntu'
21
- ruby: '2.6'
22
- env: COVERAGE=PartialSummary,Coveralls
23
-
24
- runs-on: ${{matrix.os}}-latest
25
-
26
- steps:
27
- - uses: actions/checkout@v1
28
- - uses: ruby/setup-ruby@v1
29
- with:
30
- ruby-version: ${{matrix.ruby}}
31
- - name: Install dependencies
32
- run: bundle install
33
- - name: Setup redis
34
- uses: shogo82148/actions-setup-redis@v1
35
- - name: Run tests
36
- run: ${{matrix.env}} bundle exec rspec
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/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,124 +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
- [![Actions Status](https://github.com/socketry/async-redis/workflows/Development/badge.svg)](https://github.com/socketry/async-redis/actions?workflow=Development)
6
- [![Coverage Status](https://coveralls.io/repos/socketry/async-redis/badge.svg)](https://coveralls.io/r/socketry/async-redis)
7
-
8
- [async]: https://github.com/socketry/async
9
- [async-io]: https://github.com/socketry/async-io
10
-
11
- ## Installation
12
-
13
- Add this line to your application's Gemfile:
14
-
15
- ```ruby
16
- gem 'async-redis'
17
- ```
18
-
19
- And then execute:
20
-
21
- $ bundle
22
-
23
- Or install it yourself as:
24
-
25
- $ gem install async-redis
26
-
27
- ## Usage
28
-
29
- ### Basic Local Connection
30
-
31
- ```ruby
32
- require 'async/redis'
33
-
34
- endpoint = Async::Redis.local_endpoint
35
- client = Async::Redis::Client.new(endpoint)
36
-
37
- Async do
38
- pp client.info
39
- ensure
40
- client.close
41
- end
42
- ```
43
-
44
- ### Variables
45
-
46
- ```ruby
47
- require 'async'
48
- require 'async/redis'
49
-
50
- endpoint = Async::Redis.local_endpoint
51
- client = Async::Redis::Client.new(endpoint)
52
-
53
- Async do
54
- client.set('X', 10)
55
- pp client.get('X')
56
- ensure
57
- client.close
58
- end
59
- ```
60
-
61
- ### Subscriptions
62
-
63
- ```ruby
64
- require 'async'
65
- require 'async/redis'
66
-
67
- endpoint = Async::Redis.local_endpoint
68
- client = Async::Redis::Client.new(endpoint)
69
-
70
- Async do |task|
71
- condition = Async::Condition.new
72
-
73
- publisher = task.async do
74
- condition.wait
75
-
76
- client.publish 'status.frontend', 'good'
77
- end
78
-
79
- subscriber = task.async do
80
- client.subscribe 'status.frontend' do |context|
81
- condition.signal # We are waiting for messages.
82
-
83
- type, name, message = context.listen
84
-
85
- pp type, name, message
86
- end
87
- end
88
- ensure
89
- client.close
90
- end
91
- ```
92
-
93
- ## Contributing
94
-
95
- 1. Fork it
96
- 2. Create your feature branch (`git checkout -b my-new-feature`)
97
- 3. Commit your changes (`git commit -am 'Add some feature'`)
98
- 4. Push to the branch (`git push origin my-new-feature`)
99
- 5. Create new Pull Request
100
-
101
- ## License
102
-
103
- Released under the MIT license.
104
-
105
- Copyright, 2018, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
106
- Copyright, 2018, by Huba Z. Nagy.
107
-
108
- Permission is hereby granted, free of charge, to any person obtaining a copy
109
- of this software and associated documentation files (the "Software"), to deal
110
- in the Software without restriction, including without limitation the rights
111
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
112
- copies of the Software, and to permit persons to whom the Software is
113
- furnished to do so, subject to the following conditions:
114
-
115
- The above copyright notice and this permission notice shall be included in
116
- all copies or substantial portions of the Software.
117
-
118
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
119
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
120
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
121
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
122
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
123
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
124
- 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
@@ -1,38 +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.require_paths = ["lib"]
18
-
19
- spec.add_dependency("async", "~> 1.8")
20
- spec.add_dependency("async-io", "~> 1.10")
21
- spec.add_dependency("async-pool", "~> 0.2")
22
-
23
- spec.add_dependency("protocol-redis", "~> 0.5.0")
24
-
25
- spec.add_development_dependency "async-rspec", "~> 1.1"
26
- spec.add_development_dependency "redis"
27
- spec.add_development_dependency "benchmark-ips"
28
-
29
- spec.add_development_dependency "covered"
30
- spec.add_development_dependency "bundler"
31
- spec.add_development_dependency "rspec", "~> 3.6"
32
- spec.add_development_dependency "rake"
33
-
34
- # Dependencies with C extensions
35
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby"
36
- spec.add_development_dependency "hiredis"
37
- end
38
- end