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 +4 -4
- data/lib/async/redis.rb +3 -0
- data/lib/async/redis/client.rb +14 -22
- data/lib/async/redis/context/generic.rb +7 -5
- data/lib/async/redis/context/pipeline.rb +6 -4
- data/lib/async/redis/context/subscribe.rb +10 -1
- data/lib/async/redis/context/transaction.rb +3 -1
- data/lib/async/redis/key.rb +2 -0
- data/lib/async/redis/protocol/resp2.rb +17 -1
- data/lib/async/redis/sentinels.rb +91 -0
- data/lib/async/redis/version.rb +3 -1
- metadata +40 -35
- data/.editorconfig +0 -6
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.travis.yml +0 -23
- data/Gemfile +0 -10
- data/README.md +0 -125
- data/Rakefile +0 -18
- data/async-redis.gemspec +0 -38
- data/lib/async/redis/pool.rb +0 -131
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d30c4c6db1611c8d2d65ea9ba1c807b2aecc1522ac9e936ff969db8c1aa3196d
|
4
|
+
data.tar.gz: a1acc4eabeff0c0f5f0f1a6b90353605d1c6452e43bf4da15b7c108e5de21a81
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3741bb0209230896d293dd109c86315dd154a23c488ef278ad611b8a26f6ac5e055cecbd7930d274a878507ac4c7cd722080baefb3d83425cc655beabc34ba51
|
7
|
+
data.tar.gz: 609fb8b39c3a7bf430a1aac0de4d9234f44d6f63fea22b2ecd61d57763371b8be3374a975100658b1f8bdb891fd37f3c7c0a5390769d15733648d4fa3797b596
|
data/lib/async/redis.rb
CHANGED
@@ -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'
|
data/lib/async/redis/client.rb
CHANGED
@@ -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',
|
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(*
|
58
|
-
client = self.new(*
|
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(
|
143
|
-
Pool.
|
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, *
|
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, *
|
41
|
-
@connection.write_request([command, *
|
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, *
|
51
|
-
write_request(command, *
|
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, *
|
40
|
-
@pipeline.call(command, *
|
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, *
|
76
|
-
write_request(command, *
|
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
|
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, *
|
30
|
+
def initialize(pool, *arguments)
|
29
31
|
super(pool)
|
30
32
|
end
|
31
33
|
|
data/lib/async/redis/key.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/async/redis/version.rb
CHANGED
@@ -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.
|
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
|
+
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:
|
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.
|
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.
|
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:
|
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:
|
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:
|
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:
|
141
|
+
name: rake
|
128
142
|
requirement: !ruby/object:Gem::Requirement
|
129
143
|
requirements:
|
130
|
-
- - "
|
144
|
+
- - ">="
|
131
145
|
- !ruby/object:Gem::Version
|
132
|
-
version: '
|
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: '
|
153
|
+
version: '0'
|
140
154
|
- !ruby/object:Gem::Dependency
|
141
|
-
name:
|
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:
|
169
|
+
name: rspec
|
156
170
|
requirement: !ruby/object:Gem::Requirement
|
157
171
|
requirements:
|
158
|
-
- - "
|
172
|
+
- - "~>"
|
159
173
|
- !ruby/object:Gem::Version
|
160
|
-
version: '
|
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: '
|
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.
|
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: []
|
data/.editorconfig
DELETED
data/.gitignore
DELETED
data/.rspec
DELETED
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
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
|
-
[](https://travis-ci.org/socketry/async-redis)
|
6
|
-
[](https://codeclimate.com/github/socketry/async-redis)
|
7
|
-
[](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
|
data/async-redis.gemspec
DELETED
@@ -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
|
data/lib/async/redis/pool.rb
DELETED
@@ -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
|