async-redis 0.5.0 → 0.5.1

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