specwrk-store-redis_adapter 0.0.7 → 0.2.0
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/CHANGELOG.md +6 -0
- data/RELEASING.md +95 -0
- data/lib/specwrk/store/redis_adapter/version.rb +7 -0
- data/lib/specwrk/store/redis_adapter.rb +58 -16
- metadata +21 -6
- data/specwrk-store-redis_adapter-0.0.5.gem +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d6da1ca92a3507c6251032708246c2c75490b189e24427d4541fcba13c9f1931
|
|
4
|
+
data.tar.gz: b16e5dec0899ac70e6469289a1313ab390127e0bf488cbb53a2e42afcde863d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c576a494cc6f2828973fe8c494dc880519bc70f8d0189aa0a25e3e0967b4eb86b9b808a2d8ee7acbfcfdd8ff7667aaa130c7d4cc9f3ae7c0586d28e7ac6fe510
|
|
7
|
+
data.tar.gz: f9ab432763045f76f9d5fbc887eacb5e42b8e141b82362a6a31b981aa9b75f7ea96430953aba893b4604cdc748a15f3c4bcf0685649fcbdc123f08b04f6df458
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-08-23
|
|
4
|
+
|
|
5
|
+
- Replace the Redis queue lock with atomic, token-owned Lua lock and unlock scripts cached and versioned by content SHA.
|
|
6
|
+
- Raise `Specwrk::Store::LockUnavailableError` immediately when a Redis lock cannot be acquired.
|
|
7
|
+
- Document the release process and manage versions with `gem-release`.
|
|
8
|
+
|
|
3
9
|
## [0.1.0] - 2025-08-24
|
|
4
10
|
|
|
5
11
|
- Initial release
|
data/RELEASING.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Releasing the Specwrk Redis Store Adapter
|
|
2
|
+
|
|
3
|
+
Releases are cut from `main` in two stages:
|
|
4
|
+
|
|
5
|
+
1. `gem bump` updates the version for the release.
|
|
6
|
+
2. Bundler's `rake release` task tags the commit, pushes Git, and publishes the gem.
|
|
7
|
+
|
|
8
|
+
## One-time setup
|
|
9
|
+
|
|
10
|
+
Install `gem-release`, which provides the `gem bump` command. It is intentionally
|
|
11
|
+
not a development dependency because it is only needed by maintainers cutting a
|
|
12
|
+
release.
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
gem install gem-release
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Configure credentials for RubyGems:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
gem signin
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Pushing this gem to RubyGems requires multi-factor authentication. Run the
|
|
25
|
+
release from an interactive terminal and have your authenticator available.
|
|
26
|
+
RubyGems will prompt for an OTP code or WebAuthn verification when `rake release`
|
|
27
|
+
pushes the gem. Signing in stores an API key, but does not bypass the MFA check
|
|
28
|
+
required for each push.
|
|
29
|
+
|
|
30
|
+
## Cut a release
|
|
31
|
+
|
|
32
|
+
Start from an up-to-date, clean `main` branch. This is important because
|
|
33
|
+
`rake release` pushes the current branch.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
git switch main
|
|
37
|
+
git pull --ff-only origin main
|
|
38
|
+
git status --short
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Bump the version without committing. Use `patch`, `minor`, `major`, or an
|
|
42
|
+
explicit version number:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
gem bump --version patch --no-commit
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This updates `lib/specwrk/store/redis_adapter/version.rb` while leaving the
|
|
49
|
+
release changes uncommitted. Update `CHANGELOG.md` using the new version:
|
|
50
|
+
|
|
51
|
+
1. Add a dated section for the new version.
|
|
52
|
+
2. Move the Unreleased entries into that section.
|
|
53
|
+
3. Leave an empty Unreleased section for subsequent changes.
|
|
54
|
+
|
|
55
|
+
Stage the version, changelog, and any release documentation, then create the
|
|
56
|
+
version commit:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
git add lib/specwrk/store/redis_adapter/version.rb CHANGELOG.md RELEASING.md
|
|
60
|
+
git commit -m "Bump specwrk-store-redis_adapter to VERSION"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Replace `VERSION` with the new version, for example `0.1.1`. Confirm that the
|
|
64
|
+
commit contains the version and changelog updates:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
git show --stat --oneline HEAD
|
|
68
|
+
git status --short
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Run the full checks:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
bundle exec rake
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Publish the release:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
bundle exec rake release
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The release task requires a clean tracked worktree and then:
|
|
84
|
+
|
|
85
|
+
1. Builds `pkg/specwrk-store-redis_adapter-VERSION.gem`.
|
|
86
|
+
2. Creates the annotated `vVERSION` tag.
|
|
87
|
+
3. Pushes `main` and the tag to `origin`.
|
|
88
|
+
4. Pushes the gem to RubyGems.org and prompts for MFA verification.
|
|
89
|
+
|
|
90
|
+
Use `gem bump` only to update the version; use `bundle exec rake release`, not
|
|
91
|
+
`gem release`, to publish this project.
|
|
92
|
+
|
|
93
|
+
If the RubyGems push fails after the tag was pushed, fix the authentication or
|
|
94
|
+
network problem and rerun `bundle exec rake release`. The task recognizes the
|
|
95
|
+
existing tag and retries the gem publication.
|
|
@@ -2,13 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "securerandom"
|
|
5
|
+
require "digest/sha1"
|
|
5
6
|
|
|
6
7
|
require "specwrk/store/base_adapter"
|
|
8
|
+
require "specwrk/store/serializer"
|
|
9
|
+
require "specwrk/store/redis_adapter/version"
|
|
7
10
|
require "redis-client"
|
|
8
11
|
|
|
9
12
|
module Specwrk
|
|
10
13
|
class Store
|
|
11
14
|
class RedisAdapter < Specwrk::Store::BaseAdapter
|
|
15
|
+
VERSION = REDIS_ADAPTER_VERSION
|
|
16
|
+
LOCK_TTL_MILLISECONDS = 10_000
|
|
17
|
+
LOCK_SCRIPT = <<~LUA
|
|
18
|
+
if redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) then
|
|
19
|
+
return 1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
return 0
|
|
23
|
+
LUA
|
|
24
|
+
UNLOCK_SCRIPT = <<~LUA
|
|
25
|
+
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
|
26
|
+
return redis.call("DEL", KEYS[1])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
return 0
|
|
30
|
+
LUA
|
|
31
|
+
LOCK_SCRIPT_SHA = Digest::SHA1.hexdigest(LOCK_SCRIPT)
|
|
32
|
+
UNLOCK_SCRIPT_SHA = Digest::SHA1.hexdigest(UNLOCK_SCRIPT)
|
|
33
|
+
|
|
12
34
|
@connection_pools = {}
|
|
13
35
|
@mutex = Mutex.new
|
|
14
36
|
|
|
@@ -18,23 +40,19 @@ module Specwrk
|
|
|
18
40
|
Thread.current[:connection] = connection
|
|
19
41
|
|
|
20
42
|
id = SecureRandom.uuid
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
pipeline.call("RPUSH", queue, id)
|
|
24
|
-
pipeline.call("EXPIRE", queue, 10) # only set if no expireat already
|
|
25
|
-
end
|
|
43
|
+
lock_key = "specwrk-lock-#{key}"
|
|
44
|
+
locked = false
|
|
26
45
|
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
locked = obtain_lock(connection, lock_key, id)
|
|
47
|
+
raise Specwrk::Store::LockUnavailableError unless locked
|
|
29
48
|
|
|
30
49
|
yield
|
|
31
50
|
ensure
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
begin
|
|
52
|
+
release_lock(connection, lock_key, id) if locked
|
|
53
|
+
ensure
|
|
54
|
+
Thread.current[:connection] = nil
|
|
35
55
|
end
|
|
36
|
-
|
|
37
|
-
Thread.current[:connection] = nil
|
|
38
56
|
end
|
|
39
57
|
end
|
|
40
58
|
|
|
@@ -51,18 +69,35 @@ module Specwrk
|
|
|
51
69
|
def reset_connections!
|
|
52
70
|
@connection_pools.clear
|
|
53
71
|
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def obtain_lock(connection, key, id)
|
|
76
|
+
eval_script(connection, LOCK_SCRIPT, LOCK_SCRIPT_SHA, key, id, LOCK_TTL_MILLISECONDS) == 1
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def release_lock(connection, key, id)
|
|
80
|
+
eval_script(connection, UNLOCK_SCRIPT, UNLOCK_SCRIPT_SHA, key, id)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def eval_script(connection, script, sha, key, *arguments)
|
|
84
|
+
connection.call("EVALSHA", sha, 1, key, *arguments)
|
|
85
|
+
rescue RedisClient::NoScriptError
|
|
86
|
+
connection.call("SCRIPT", "LOAD", script)
|
|
87
|
+
retry
|
|
88
|
+
end
|
|
54
89
|
end
|
|
55
90
|
|
|
56
91
|
def [](key)
|
|
57
92
|
with_connection do |redis|
|
|
58
93
|
value = redis.call("HGET", scope, key)
|
|
59
|
-
|
|
94
|
+
self.class.serializer.load(value) if value
|
|
60
95
|
end
|
|
61
96
|
end
|
|
62
97
|
|
|
63
98
|
def []=(key, value)
|
|
64
99
|
with_connection do |redis|
|
|
65
|
-
redis.call("HSET", scope, key,
|
|
100
|
+
redis.call("HSET", scope, key, self.class.serializer.dump(value))
|
|
66
101
|
end
|
|
67
102
|
end
|
|
68
103
|
|
|
@@ -101,7 +136,7 @@ module Specwrk
|
|
|
101
136
|
|
|
102
137
|
read_keys.zip(values).each do |key, value|
|
|
103
138
|
next if value.nil?
|
|
104
|
-
result[key] =
|
|
139
|
+
result[key] = self.class.serializer.load(value)
|
|
105
140
|
end
|
|
106
141
|
|
|
107
142
|
result
|
|
@@ -111,7 +146,7 @@ module Specwrk
|
|
|
111
146
|
return if hash.nil? || hash.length.zero?
|
|
112
147
|
|
|
113
148
|
with_connection do |redis|
|
|
114
|
-
redis.call("HMSET", scope, *hash.flat_map { |key, value| [key,
|
|
149
|
+
redis.call("HMSET", scope, *hash.flat_map { |key, value| [key, self.class.serializer.dump(value)] })
|
|
115
150
|
end
|
|
116
151
|
end
|
|
117
152
|
|
|
@@ -132,6 +167,13 @@ module Specwrk
|
|
|
132
167
|
end
|
|
133
168
|
end
|
|
134
169
|
end
|
|
170
|
+
|
|
171
|
+
# override
|
|
172
|
+
def scope
|
|
173
|
+
return @scope if self.class.serializer.adapter_name == "json"
|
|
174
|
+
|
|
175
|
+
[self.class.serializer.adapter_name, @scope].join("-")
|
|
176
|
+
end
|
|
135
177
|
end
|
|
136
178
|
end
|
|
137
179
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: specwrk-store-redis_adapter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Westendorf
|
|
@@ -11,6 +11,20 @@ date: 1980-01-02 00:00:00.000000000 Z
|
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: specwrk
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.19.4
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.19.4
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: redis-client
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
15
29
|
requirements:
|
|
16
30
|
- - ">="
|
|
@@ -24,13 +38,13 @@ dependencies:
|
|
|
24
38
|
- !ruby/object:Gem::Version
|
|
25
39
|
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
41
|
+
name: standard
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
29
43
|
requirements:
|
|
30
44
|
- - ">="
|
|
31
45
|
- !ruby/object:Gem::Version
|
|
32
46
|
version: '0'
|
|
33
|
-
type: :
|
|
47
|
+
type: :development
|
|
34
48
|
prerelease: false
|
|
35
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
50
|
requirements:
|
|
@@ -38,7 +52,7 @@ dependencies:
|
|
|
38
52
|
- !ruby/object:Gem::Version
|
|
39
53
|
version: '0'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
55
|
+
name: msgpack
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
43
57
|
requirements:
|
|
44
58
|
- - ">="
|
|
@@ -62,10 +76,11 @@ files:
|
|
|
62
76
|
- CHANGELOG.md
|
|
63
77
|
- LICENSE
|
|
64
78
|
- README.md
|
|
79
|
+
- RELEASING.md
|
|
65
80
|
- Rakefile
|
|
66
81
|
- docker-compose.yml
|
|
67
82
|
- lib/specwrk/store/redis_adapter.rb
|
|
68
|
-
- specwrk
|
|
83
|
+
- lib/specwrk/store/redis_adapter/version.rb
|
|
69
84
|
homepage: https://github.com/danielwestendorf/specwrk-store-redis_adapter
|
|
70
85
|
licenses:
|
|
71
86
|
- GPL-3.0-or-later
|
|
@@ -87,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
87
102
|
- !ruby/object:Gem::Version
|
|
88
103
|
version: '0'
|
|
89
104
|
requirements: []
|
|
90
|
-
rubygems_version:
|
|
105
|
+
rubygems_version: 4.0.10
|
|
91
106
|
specification_version: 4
|
|
92
107
|
summary: Redis adapater for Specwrk, a parallel RSpec test runner
|
|
93
108
|
test_files: []
|
|
Binary file
|