specwrk-store-redis_adapter 0.0.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 +7 -0
- data/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +5 -0
- data/README.md +3 -0
- data/Rakefile +10 -0
- data/docker-compose.yml +6 -0
- data/lib/specwrk/store/redis_adapter.rb +166 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2774f66f6a6356247c90f8dae3cc57452a0389ed502d4713ddd5d3ccc5c3f9cb
|
4
|
+
data.tar.gz: 2ff23a77e09dab33ae4518e2fffa96af8d1893349278615542074073859d8ed2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 785340c5eaf3af5540b4eb0ed60d95ff79ed98574bdc1734822cce33091327b99b9f27579ea233c3d9682bc471257906d6da2e9127c2f1267daf607871e75085
|
7
|
+
data.tar.gz: 156a21fb6424a180af03989bcbb3973e5825b32664ab9e1fc77c511124643c58b03f8bb8636ef00153721a458302e9b7a2fbaa09e87fe4919492b3409a343c4c
|
data/.rspec
ADDED
data/.standard.yml
ADDED
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
require "specwrk/store/base_adapter"
|
6
|
+
require "redis-client"
|
7
|
+
require "redlock"
|
8
|
+
|
9
|
+
module Specwrk
|
10
|
+
class Store
|
11
|
+
class RedisAdapter < Specwrk::Store::BaseAdapter
|
12
|
+
VERSION = "0.1.0"
|
13
|
+
|
14
|
+
REDIS_KEY_DELIMITER = "||||"
|
15
|
+
|
16
|
+
@connection_pools = {}
|
17
|
+
@mutex = Mutex.new
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def with_lock(uri, key)
|
21
|
+
connection_pool_for(uri).with do |connection|
|
22
|
+
client = Redlock::Client.new(
|
23
|
+
[connection],
|
24
|
+
retry_count: lock_retry_count,
|
25
|
+
retry_delay: lock_retry_delay,
|
26
|
+
retry_jitter: lock_retry_jitter
|
27
|
+
)
|
28
|
+
|
29
|
+
until (lock = client.lock("specwrk-lock-#{key}", lock_ttl))
|
30
|
+
sleep(rand(0.001..0.09))
|
31
|
+
end
|
32
|
+
|
33
|
+
yield
|
34
|
+
|
35
|
+
client.unlock(lock)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def connection_pool_for(uri)
|
40
|
+
return @connection_pools[uri] if @connection_pools.key? uri
|
41
|
+
|
42
|
+
@mutex.synchronize do
|
43
|
+
@connection_pools[uri] ||= RedisClient.config(url: uri).new_pool(
|
44
|
+
size: ENV.fetch("SPECWRK_THREAD_COUNT", "4").to_i
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def reset_connections!
|
50
|
+
@connection_pools.clear
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# In ms
|
56
|
+
def lock_ttl
|
57
|
+
ENV.fetch("SPECWRK_REDIS_ADAPTER_LOCK_TTL", "5000").to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
# See https://www.rubydoc.info/gems/redlock/Redlock/Client#initialize-instance_method
|
61
|
+
def lock_retry_count
|
62
|
+
ENV.fetch("SPECWRK_REDIS_ADAPTER_LOCK_RETRY_COUNT", "3").to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
def lock_retry_delay
|
66
|
+
ENV.fetch("SPECWRK_REDIS_ADAPTER_LOCK_RETRY_DELAY", "100").to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def lock_retry_jitter
|
70
|
+
ENV.fetch("SPECWRK_REDIS_ADAPTER_LOCK_RETRY_JITTER", "10").to_i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def [](key)
|
75
|
+
with_connection do |redis|
|
76
|
+
value = redis.call("GET", encode_key(key))
|
77
|
+
JSON.parse(value, symbolize_names: true) if value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def []=(key, value)
|
82
|
+
with_connection do |redis|
|
83
|
+
redis.call("SET", encode_key(key), JSON.generate(value))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def keys
|
88
|
+
[].tap do |collected|
|
89
|
+
scan_for("#{scope}#{REDIS_KEY_DELIMITER}*") { |k| collected << decode_key(k) }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def clear
|
94
|
+
delete(*keys)
|
95
|
+
end
|
96
|
+
|
97
|
+
def delete(*keys)
|
98
|
+
return if keys.length.zero?
|
99
|
+
|
100
|
+
with_connection do |redis|
|
101
|
+
redis.call("DEL", *keys.map { |key| encode_key key })
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def merge!(h2)
|
106
|
+
multi_write(h2)
|
107
|
+
end
|
108
|
+
|
109
|
+
def multi_read(*read_keys)
|
110
|
+
return {} if read_keys.length.zero?
|
111
|
+
|
112
|
+
values = with_connection do |redis|
|
113
|
+
redis.call("MGET", *read_keys.map { |key| encode_key(key) })
|
114
|
+
end
|
115
|
+
|
116
|
+
result = {}
|
117
|
+
|
118
|
+
read_keys.zip(values).each do |key, value|
|
119
|
+
next if value.nil?
|
120
|
+
result[key] = JSON.parse(value, symbolize_names: true)
|
121
|
+
end
|
122
|
+
|
123
|
+
result
|
124
|
+
end
|
125
|
+
|
126
|
+
def multi_write(hash)
|
127
|
+
return if hash.nil? || hash.length.zero?
|
128
|
+
|
129
|
+
with_connection do |redis|
|
130
|
+
redis.call("MSET", *hash.flat_map { |key, value| [encode_key(key), JSON.generate(value)] })
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def empty?
|
135
|
+
keys.length.zero?
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def with_connection
|
141
|
+
self.class.connection_pool_for(uri).with do |connection|
|
142
|
+
yield connection
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def encode_key(key)
|
147
|
+
[scope, REDIS_KEY_DELIMITER, key].join
|
148
|
+
end
|
149
|
+
|
150
|
+
def decode_key(key)
|
151
|
+
key.split(REDIS_KEY_DELIMITER).last
|
152
|
+
end
|
153
|
+
|
154
|
+
def scan_for(match)
|
155
|
+
with_connection do |redis|
|
156
|
+
cursor = "0"
|
157
|
+
loop do
|
158
|
+
cursor, batch = redis.call("SCAN", cursor, "MATCH", match, "COUNT", 5_000)
|
159
|
+
batch.each { |k| yield k }
|
160
|
+
break if cursor == "0"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: specwrk-store-redis_adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Westendorf
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: specwrk
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: redis-client
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: redlock
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: standard
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
email:
|
69
|
+
- daniel@prowestech.com
|
70
|
+
executables: []
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files: []
|
73
|
+
files:
|
74
|
+
- ".rspec"
|
75
|
+
- ".standard.yml"
|
76
|
+
- CHANGELOG.md
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- docker-compose.yml
|
81
|
+
- lib/specwrk/store/redis_adapter.rb
|
82
|
+
homepage: https://github.com/danielwestendorf/specwrk-store-redis_adapter
|
83
|
+
licenses:
|
84
|
+
- GPL-3.0-or-later
|
85
|
+
metadata:
|
86
|
+
homepage_uri: https://github.com/danielwestendorf/specwrk-store-redis_adapter
|
87
|
+
source_code_uri: https://github.com/danielwestendorf/specwrk-store-redis_adapter
|
88
|
+
changelog_uri: https://github.com/danielwestendorf/specwrk-store-redis_adapter/blob/main/CHANGELOG.md
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.0.0
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubygems_version: 3.6.9
|
104
|
+
specification_version: 4
|
105
|
+
summary: Redis adapater for Specwrk, a parallel RSpec test runner
|
106
|
+
test_files: []
|