async-redis 0.4.1 → 0.5.2

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: d1cabed5b3d53ceb51ba113cffb2ace9bab46a1eacefc159531fc24723598c29
4
- data.tar.gz: 566ea90f81848a6c78bb468e351e0c61ddbd01c82d76f9855dbed69ef0016cfc
3
+ metadata.gz: 7796495846ed0704472b1daea7b68b6ef8eaab48b7fa7f70699603eb3b88f834
4
+ data.tar.gz: cfbe584b55ddf465e4a013512003971df7c51ef8fe118b3843f1113d08bee7c4
5
5
  SHA512:
6
- metadata.gz: d2cd6cddf7bf21fc8b71b86ed6739bf9799378a264f27ccb867181e90d298f0357569070080737106674808b738e17b317ef1fb5c1a23c00f47cc2bde86324dd
7
- data.tar.gz: 32cbe030112f49d1fd498804b4756c1108fc3d985bafdcb249507d23b1c4e17372925d0a05e52e55bf67a110bada60f863ca838135f2bdb0d48afc4cccdb02fd
6
+ metadata.gz: 54523c3cb2c25376f46ce385c4bd2eb3a575993979810e4017b6e057de55811fb6d8c87d081f6677644307d967df5497415b8b155618def43d6eb7692c310ea1
7
+ data.tar.gz: 2525dd0c0cd9eb602253513091ad68fe83350061ed1753fb70c28f9ac5dc75a627234837a2c5a2dc3fb5d7aa8d7331218f833d050c95d6329204c2663d9fa161
@@ -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
 
@@ -72,10 +74,6 @@ module Async
72
74
  @pool.close
73
75
  end
74
76
 
75
- def publish(channel, message)
76
- call('PUBLISH', channel, message)
77
- end
78
-
79
77
  def subscribe(*channels)
80
78
  context = Context::Subscribe.new(@pool, channels)
81
79
 
@@ -88,18 +86,6 @@ module Async
88
86
  end
89
87
  end
90
88
 
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
89
  def transaction(&block)
104
90
  context = Context::Transaction.new(@pool)
105
91
 
@@ -111,12 +97,14 @@ module Async
111
97
  context.close
112
98
  end
113
99
  end
114
-
100
+
101
+ alias multi transaction
102
+
115
103
  def pipeline(&block)
116
104
  context = Context::Pipeline.new(@pool)
117
-
105
+
118
106
  return context unless block_given?
119
-
107
+
120
108
  begin
121
109
  yield context
122
110
  ensure
@@ -139,8 +127,8 @@ module Async
139
127
 
140
128
  protected
141
129
 
142
- def connect(connection_limit: nil)
143
- Pool.new(connection_limit) do
130
+ def connect(**options)
131
+ Async::Pool::Controller.wrap(**options) do
144
132
  peer = @endpoint.connect
145
133
 
146
134
  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
@@ -25,9 +27,17 @@ module Async
25
27
  module Protocol
26
28
  module RESP2
27
29
  class Connection < ::Protocol::Redis::Connection
28
- def connected?
30
+ def concurrency
31
+ 1
32
+ end
33
+
34
+ def viable?
29
35
  @stream.connected?
30
36
  end
37
+
38
+ def reusable?
39
+ !@stream.closed?
40
+ end
31
41
  end
32
42
 
33
43
  def self.client(stream)
@@ -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.1"
25
+ VERSION = "0.5.2"
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.1
4
+ version: 0.5.2
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-10-09 00:00:00.000000000 Z
12
+ date: 2020-11-17 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.6.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.6.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.4
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,135 +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
- while resource = @resources.pop
116
- if resource.connected?
117
- return resource
118
- else
119
- retire(resource)
120
- end
121
- end
122
-
123
- if !@limit or @active < @limit
124
- Async.logger.debug(self) {"No resources resources, allocating new one..."}
125
-
126
- @active += 1
127
-
128
- return create
129
- end
130
-
131
- return nil
132
- end
133
- end
134
- end
135
- end