async-redis 0.4.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: c94ef3534e5271a37e5588ac9906f88e421f03db3a3251de8e3f0b8aa414f91a
4
- data.tar.gz: d8503520edb25b468aed41db06ce61a38ddb753bf5ec8034f9b129ec60e2a600
3
+ metadata.gz: d30c4c6db1611c8d2d65ea9ba1c807b2aecc1522ac9e936ff969db8c1aa3196d
4
+ data.tar.gz: a1acc4eabeff0c0f5f0f1a6b90353605d1c6452e43bf4da15b7c108e5de21a81
5
5
  SHA512:
6
- metadata.gz: b2926a1fccbba8221ed83b9b46e62f82fc2f4a44104e74d1b29973124375305eb3a3da0cdeef03071adb6b967ab7d72f2747829a243e20db9bebb34139973370
7
- data.tar.gz: 57636bc3338aad4c34aa411034a67a586e753e0729c3436b290c9d84a59387b8d2c69a4be8f72f48b34a9f2590d2186c9944eba78fecbfa4c31bfc4f56681177
6
+ metadata.gz: 3741bb0209230896d293dd109c86315dd154a23c488ef278ad611b8a26f6ac5e055cecbd7930d274a878507ac4c7cd722080baefb3d83425cc655beabc34ba51
7
+ data.tar.gz: 609fb8b39c3a7bf430a1aac0de4d9234f44d6f63fea22b2ecd61d57763371b8be3374a975100658b1f8bdb891fd37f3c7c0a5390769d15733648d4fa3797b596
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -20,3 +22,4 @@
20
22
 
21
23
  require_relative 'redis/version'
22
24
  require_relative 'redis/client'
25
+ require_relative 'redis/sentinels'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -18,7 +20,6 @@
18
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
21
  # THE SOFTWARE.
20
22
 
21
- require_relative 'pool'
22
23
  require_relative 'context/pipeline'
23
24
  require_relative 'context/transaction'
24
25
  require_relative 'context/subscribe'
@@ -27,6 +28,7 @@ require_relative 'protocol/resp2'
27
28
 
28
29
  require 'async/io'
29
30
  require 'async/io/stream'
31
+ require 'async/pool/controller'
30
32
 
31
33
  require 'protocol/redis/methods'
32
34
 
@@ -35,8 +37,8 @@ module Async
35
37
  # Legacy.
36
38
  ServerError = ::Protocol::Redis::ServerError
37
39
 
38
- def self.local_endpoint
39
- Async::IO::Endpoint.tcp('localhost', 6379)
40
+ def self.local_endpoint(port: 6379)
41
+ Async::IO::Endpoint.tcp('localhost', port)
40
42
  end
41
43
 
42
44
  class Client
@@ -54,8 +56,8 @@ module Async
54
56
 
55
57
  # @return [client] if no block provided.
56
58
  # @yield [client, task] yield the client in an async task.
57
- def self.open(*args, &block)
58
- client = self.new(*args)
59
+ def self.open(*arguments, &block)
60
+ client = self.new(*arguments)
59
61
 
60
62
  return client unless block_given?
61
63
 
@@ -88,18 +90,6 @@ module Async
88
90
  end
89
91
  end
90
92
 
91
- def multi(&block)
92
- context = Context::Multi.new(@pool)
93
-
94
- return context unless block_given?
95
-
96
- begin
97
- yield context
98
- ensure
99
- context.close
100
- end
101
- end
102
-
103
93
  def transaction(&block)
104
94
  context = Context::Transaction.new(@pool)
105
95
 
@@ -111,12 +101,14 @@ module Async
111
101
  context.close
112
102
  end
113
103
  end
114
-
104
+
105
+ alias multi transaction
106
+
115
107
  def pipeline(&block)
116
108
  context = Context::Pipeline.new(@pool)
117
-
109
+
118
110
  return context unless block_given?
119
-
111
+
120
112
  begin
121
113
  yield context
122
114
  ensure
@@ -139,8 +131,8 @@ module Async
139
131
 
140
132
  protected
141
133
 
142
- def connect(connection_limit: nil)
143
- Pool.new(connection_limit) do
134
+ def connect(**options)
135
+ Async::Pool::Controller.wrap(**options) do
144
136
  peer = @endpoint.connect
145
137
 
146
138
  peer.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  # Copyright, 2018, by Huba Nagy.
3
5
  #
@@ -25,7 +27,7 @@ module Async
25
27
  module Redis
26
28
  module Context
27
29
  class Generic
28
- def initialize(pool, *args)
30
+ def initialize(pool, *arguments)
29
31
  @pool = pool
30
32
  @connection = pool.acquire
31
33
  end
@@ -37,8 +39,8 @@ module Async
37
39
  end
38
40
  end
39
41
 
40
- def write_request(command, *args)
41
- @connection.write_request([command, *args])
42
+ def write_request(command, *arguments)
43
+ @connection.write_request([command, *arguments])
42
44
  end
43
45
 
44
46
  def read_response
@@ -47,8 +49,8 @@ module Async
47
49
  return @connection.read_response
48
50
  end
49
51
 
50
- def call(command, *args)
51
- write_request(command, *args)
52
+ def call(command, *arguments)
53
+ write_request(command, *arguments)
52
54
 
53
55
  return read_response
54
56
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  # Copyright, 2019, by Huba Nagy.
3
5
  #
@@ -36,8 +38,8 @@ module Async
36
38
  end
37
39
 
38
40
  # This method just accumulates the commands and their params.
39
- def call(command, *args)
40
- @pipeline.call(command, *args)
41
+ def call(command, *arguments)
42
+ @pipeline.call(command, *arguments)
41
43
 
42
44
  @pipeline.flush(1)
43
45
 
@@ -72,8 +74,8 @@ module Async
72
74
  end
73
75
 
74
76
  # This method just accumulates the commands and their params.
75
- def call(command, *args)
76
- write_request(command, *args)
77
+ def call(command, *arguments)
78
+ write_request(command, *arguments)
77
79
 
78
80
  return nil
79
81
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  # Copyright, 2018, by Huba Nagy.
3
5
  #
@@ -33,6 +35,13 @@ module Async
33
35
  subscribe(channels)
34
36
  end
35
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
+
36
45
  def listen
37
46
  return @connection.read_response
38
47
  end
@@ -65,7 +74,7 @@ module Async
65
74
  return response
66
75
  else
67
76
  @channels.subtract(channels)
68
- return call 'UNSUBSCRIBE', *channels
77
+ return call('UNSUBSCRIBE', *channels)
69
78
  end
70
79
  end
71
80
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  # Copyright, 2018, by Huba Nagy.
3
5
  #
@@ -25,7 +27,7 @@ module Async
25
27
  module Redis
26
28
  module Context
27
29
  class Transaction < Pipeline
28
- def initialize(pool, *args)
30
+ def initialize(pool, *arguments)
29
31
  super(pool)
30
32
  end
31
33
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -24,8 +26,22 @@ module Async
24
26
  module Redis
25
27
  module Protocol
26
28
  module RESP2
29
+ class Connection < ::Protocol::Redis::Connection
30
+ def concurrency
31
+ 1
32
+ end
33
+
34
+ def viable?
35
+ @stream.connected?
36
+ end
37
+
38
+ def reusable?
39
+ !@stream.closed?
40
+ end
41
+ end
42
+
27
43
  def self.client(stream)
28
- ::Protocol::Redis::Connection.new(stream)
44
+ Connection.new(stream)
29
45
  end
30
46
  end
31
47
  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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -20,6 +22,6 @@
20
22
 
21
23
  module Async
22
24
  module Redis
23
- VERSION = "0.4.0"
25
+ VERSION = "0.5.1"
24
26
  end
25
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.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: 2019-08-29 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
@@ -39,20 +39,34 @@ dependencies:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '1.10'
42
+ - !ruby/object:Gem::Dependency
43
+ name: async-pool
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.2'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.2'
42
56
  - !ruby/object:Gem::Dependency
43
57
  name: protocol-redis
44
58
  requirement: !ruby/object:Gem::Requirement
45
59
  requirements:
46
60
  - - "~>"
47
61
  - !ruby/object:Gem::Version
48
- version: 0.2.0
62
+ version: 0.5.0
49
63
  type: :runtime
50
64
  prerelease: false
51
65
  version_requirements: !ruby/object:Gem::Requirement
52
66
  requirements:
53
67
  - - "~>"
54
68
  - !ruby/object:Gem::Version
55
- version: 0.2.0
69
+ version: 0.5.0
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: async-rspec
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -68,7 +82,7 @@ dependencies:
68
82
  - !ruby/object:Gem::Version
69
83
  version: '1.1'
70
84
  - !ruby/object:Gem::Dependency
71
- name: redis
85
+ name: benchmark-ips
72
86
  requirement: !ruby/object:Gem::Requirement
73
87
  requirements:
74
88
  - - ">="
@@ -82,7 +96,7 @@ dependencies:
82
96
  - !ruby/object:Gem::Version
83
97
  version: '0'
84
98
  - !ruby/object:Gem::Dependency
85
- name: benchmark-ips
99
+ name: bundler
86
100
  requirement: !ruby/object:Gem::Requirement
87
101
  requirements:
88
102
  - - ">="
@@ -110,7 +124,7 @@ dependencies:
110
124
  - !ruby/object:Gem::Version
111
125
  version: '0'
112
126
  - !ruby/object:Gem::Dependency
113
- name: bundler
127
+ name: hiredis
114
128
  requirement: !ruby/object:Gem::Requirement
115
129
  requirements:
116
130
  - - ">="
@@ -124,21 +138,21 @@ dependencies:
124
138
  - !ruby/object:Gem::Version
125
139
  version: '0'
126
140
  - !ruby/object:Gem::Dependency
127
- name: rspec
141
+ name: rake
128
142
  requirement: !ruby/object:Gem::Requirement
129
143
  requirements:
130
- - - "~>"
144
+ - - ">="
131
145
  - !ruby/object:Gem::Version
132
- version: '3.6'
146
+ version: '0'
133
147
  type: :development
134
148
  prerelease: false
135
149
  version_requirements: !ruby/object:Gem::Requirement
136
150
  requirements:
137
- - - "~>"
151
+ - - ">="
138
152
  - !ruby/object:Gem::Version
139
- version: '3.6'
153
+ version: '0'
140
154
  - !ruby/object:Gem::Dependency
141
- name: rake
155
+ name: redis
142
156
  requirement: !ruby/object:Gem::Requirement
143
157
  requirements:
144
158
  - - ">="
@@ -152,35 +166,25 @@ dependencies:
152
166
  - !ruby/object:Gem::Version
153
167
  version: '0'
154
168
  - !ruby/object:Gem::Dependency
155
- name: hiredis
169
+ name: rspec
156
170
  requirement: !ruby/object:Gem::Requirement
157
171
  requirements:
158
- - - ">="
172
+ - - "~>"
159
173
  - !ruby/object:Gem::Version
160
- version: '0'
174
+ version: '3.6'
161
175
  type: :development
162
176
  prerelease: false
163
177
  version_requirements: !ruby/object:Gem::Requirement
164
178
  requirements:
165
- - - ">="
179
+ - - "~>"
166
180
  - !ruby/object:Gem::Version
167
- version: '0'
168
- description:
181
+ version: '3.6'
182
+ description:
169
183
  email:
170
- - samuel.williams@oriontransfer.co.nz
171
- - 12huba@gmail.com
172
184
  executables: []
173
185
  extensions: []
174
186
  extra_rdoc_files: []
175
187
  files:
176
- - ".editorconfig"
177
- - ".gitignore"
178
- - ".rspec"
179
- - ".travis.yml"
180
- - Gemfile
181
- - README.md
182
- - Rakefile
183
- - async-redis.gemspec
184
188
  - lib/async/redis.rb
185
189
  - lib/async/redis/client.rb
186
190
  - lib/async/redis/context/generic.rb
@@ -188,13 +192,14 @@ files:
188
192
  - lib/async/redis/context/subscribe.rb
189
193
  - lib/async/redis/context/transaction.rb
190
194
  - lib/async/redis/key.rb
191
- - lib/async/redis/pool.rb
192
195
  - lib/async/redis/protocol/resp2.rb
196
+ - lib/async/redis/sentinels.rb
193
197
  - lib/async/redis/version.rb
194
198
  homepage: https://github.com/socketry/async-redis
195
- licenses: []
199
+ licenses:
200
+ - MIT
196
201
  metadata: {}
197
- post_install_message:
202
+ post_install_message:
198
203
  rdoc_options: []
199
204
  require_paths:
200
205
  - lib
@@ -209,8 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
214
  - !ruby/object:Gem::Version
210
215
  version: '0'
211
216
  requirements: []
212
- rubygems_version: 3.0.3
213
- signing_key:
217
+ rubygems_version: 3.1.2
218
+ signing_key:
214
219
  specification_version: 4
215
220
  summary: A Redis client library.
216
221
  test_files: []
@@ -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
@@ -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,10 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in async-io.gemspec
4
- gemspec
5
-
6
- # gem "protocol-redis", path: "../protocol-redis"
7
-
8
- group :development do
9
- gem 'pry'
10
- 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,18 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:test)
5
-
6
- task :default => :test
7
-
8
- task :client do
9
- require 'irb'
10
- require 'async/redis/client'
11
-
12
- endpoint = Async::Redis.local_endpoint
13
- client = Async::Redis::Client.new(endpoint)
14
-
15
- Async do
16
- binding.irb
17
- end
18
- 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.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
-
23
- spec.add_dependency("protocol-redis", "~> 0.2.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
@@ -1,131 +0,0 @@
1
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'async/notification'
22
-
23
- module Async
24
- module Redis
25
- # It might make sense to add support for pipelining https://redis.io/topics/pipelining
26
- # We should be able to wrap the protocol so that write_request and read_response happen in lockstep.
27
- # The only problem would be blocking operations. It might also be confusing if order of operations affects commands.
28
- class Pool
29
- def initialize(limit = nil, &block)
30
- @resources = []
31
- @available = Async::Notification.new
32
-
33
- @limit = limit
34
- @active = 0
35
-
36
- @constructor = block
37
- end
38
-
39
- attr :resources
40
-
41
- def empty?
42
- @resources.empty?
43
- end
44
-
45
- def acquire
46
- resource = wait_for_resource
47
-
48
- return resource unless block_given?
49
-
50
- begin
51
- yield resource
52
- ensure
53
- release(resource)
54
- end
55
- end
56
-
57
- # Make the resource resources and let waiting tasks know that there is something resources.
58
- def release(resource)
59
- # A resource that is not good should also not be reusable.
60
- unless resource.closed?
61
- reuse(resource)
62
- else
63
- retire(resource)
64
- end
65
- end
66
-
67
- def close
68
- @resources.each(&:close)
69
- @resources.clear
70
-
71
- @active = 0
72
- end
73
-
74
- def to_s
75
- "\#<#{self.class} resources=#{resources.size} limit=#{@limit}>"
76
- end
77
-
78
- protected
79
-
80
- def reuse(resource)
81
- Async.logger.debug(self) {"Reuse #{resource}"}
82
-
83
- @resources << resource
84
-
85
- @available.signal
86
- end
87
-
88
- def retire(resource)
89
- Async.logger.debug(self) {"Retire #{resource}"}
90
-
91
- @active -= 1
92
-
93
- resource.close
94
-
95
- @available.signal
96
- end
97
-
98
- def wait_for_resource
99
- # If we fail to create a resource (below), we will end up waiting for one to become resources.
100
- until resource = available_resource
101
- @available.wait
102
- end
103
-
104
- Async.logger.debug(self) {"Wait for resource #{resource}"}
105
-
106
- return resource
107
- end
108
-
109
- def create
110
- # This might return nil, which means creating the resource failed.
111
- return @constructor.call
112
- end
113
-
114
- def available_resource
115
- if @resources.any?
116
- return @resources.pop
117
- end
118
-
119
- if !@limit or @active < @limit
120
- Async.logger.debug(self) {"No resources resources, allocating new one..."}
121
-
122
- @active += 1
123
-
124
- return create
125
- end
126
-
127
- return nil
128
- end
129
- end
130
- end
131
- end