philiprehberger-lock_kit 0.3.0 → 0.5.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 +12 -0
- data/README.md +18 -0
- data/lib/philiprehberger/lock_kit/read_write_lock.rb +10 -2
- data/lib/philiprehberger/lock_kit/version.rb +1 -1
- data/lib/philiprehberger/lock_kit.rb +37 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd5f40b2057061170fc5913d9ac238f7070d3fb05025e9f5bffe2e321e3b240b
|
|
4
|
+
data.tar.gz: ece6023899b7329973758c48b3f7da96937e9cdc700ec4a11f0e572a7f9d4246
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b57abbeb1216f641fbe8d386425df778fd50a9aadc64be51f98df41e3f47409e6886c0bfe588cde71223b73a684c7da5c7e4c14996c30e66f70961d0fd8bec3
|
|
7
|
+
data.tar.gz: 4e85ef77b8c6c6bdb50f327a8ddf0841f19754e40ec20b1cfa52e69b7b5ef1127cf8426ed4c4f5e4584c2ff3e54bc13ed8905ab6cd5a15b68da83d7925966d81
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-04-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `ReadWriteLock#write_locked?` promoted to a public non-blocking query method
|
|
14
|
+
- `ReadWriteLock#stats` returning `{ readers:, write_locked: }` snapshot
|
|
15
|
+
- `LockKit.rw_stats(path)` module-level helper for the same snapshot without an instance
|
|
16
|
+
|
|
17
|
+
## [0.4.0] - 2026-04-15
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `with_retry_lock(path, retries: 3, delay: 0.1, backoff: 2, timeout: nil, auto_cleanup: true, ttl: nil)` module method for lock acquisition with exponential backoff retries
|
|
21
|
+
|
|
10
22
|
## [0.3.0] - 2026-04-10
|
|
11
23
|
|
|
12
24
|
### Added
|
data/README.md
CHANGED
|
@@ -64,6 +64,20 @@ Philiprehberger::LockKit.with_write_lock('/tmp/data.lock', timeout: 5) do
|
|
|
64
64
|
end
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
### Retry Lock
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
# Retries lock acquisition with exponential backoff
|
|
71
|
+
Philiprehberger::LockKit.with_retry_lock('/tmp/my.lock', retries: 5, delay: 0.2, backoff: 2) do
|
|
72
|
+
# acquired after retrying if initially contended
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# With timeout per attempt, TTL, and custom cleanup
|
|
76
|
+
Philiprehberger::LockKit.with_retry_lock('/tmp/my.lock', retries: 3, delay: 0.1, backoff: 2, timeout: 5, ttl: 30) do
|
|
77
|
+
# each attempt waits up to 5s; lock expires after 30s
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
67
81
|
### Lock with TTL (Time-to-Live)
|
|
68
82
|
|
|
69
83
|
```ruby
|
|
@@ -151,6 +165,7 @@ Philiprehberger::LockKit.stale?('/tmp/my_worker.pid') # => true/false
|
|
|
151
165
|
| Method | Description |
|
|
152
166
|
|--------|-------------|
|
|
153
167
|
| `.with_file_lock(path, timeout: nil, auto_cleanup: false, on_wait: nil, ttl: nil) { }` | Execute block with exclusive file lock |
|
|
168
|
+
| `.with_retry_lock(path, retries: 3, delay: 0.1, backoff: 2, timeout: nil, auto_cleanup: true, ttl: nil) { }` | Execute block with file lock, retrying with exponential backoff |
|
|
154
169
|
| `.with_pid_lock(name, dir: Dir.tmpdir, auto_cleanup: false, ttl: nil) { }` | Execute block with PID file lock |
|
|
155
170
|
| `.with_read_lock(path, timeout: nil) { }` | Execute block with shared read lock |
|
|
156
171
|
| `.with_write_lock(path, timeout: nil) { }` | Execute block with exclusive write lock |
|
|
@@ -159,6 +174,7 @@ Philiprehberger::LockKit.stale?('/tmp/my_worker.pid') # => true/false
|
|
|
159
174
|
| `.expired?(path)` | Check if a lock has expired based on its TTL |
|
|
160
175
|
| `.owner(path)` | Get lock owner metadata (pid, hostname, acquired_at) |
|
|
161
176
|
| `.break!(path, force: false)` | Break a lock (stale only by default, any with force) |
|
|
177
|
+
| `.rw_stats(path)` | Snapshot of a read-write lock as `{ readers:, write_locked: }` |
|
|
162
178
|
|
|
163
179
|
### `LockKit::FileLock`
|
|
164
180
|
|
|
@@ -193,6 +209,8 @@ Philiprehberger::LockKit.stale?('/tmp/my_worker.pid') # => true/false
|
|
|
193
209
|
| `#acquire_write(timeout: nil)` | Acquire exclusive write lock |
|
|
194
210
|
| `#release_write` | Release the write lock |
|
|
195
211
|
| `#reader_count` | Get current number of active readers |
|
|
212
|
+
| `#write_locked?` | Non-blocking check for an active write lock |
|
|
213
|
+
| `#stats` | Snapshot as `{ readers:, write_locked: }` |
|
|
196
214
|
|
|
197
215
|
## Development
|
|
198
216
|
|
|
@@ -126,8 +126,9 @@ module Philiprehberger
|
|
|
126
126
|
0
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
# Non-blocking check for whether a write lock is currently held.
|
|
130
|
+
#
|
|
131
|
+
# @return [Boolean]
|
|
131
132
|
def write_locked?
|
|
132
133
|
return false unless File.exist?(@write_lock_path)
|
|
133
134
|
|
|
@@ -143,6 +144,13 @@ module Philiprehberger
|
|
|
143
144
|
end
|
|
144
145
|
end
|
|
145
146
|
|
|
147
|
+
# Snapshot of the current lock state: readers and writer presence.
|
|
148
|
+
#
|
|
149
|
+
# @return [Hash] `{ readers: Integer, write_locked: Boolean }`
|
|
150
|
+
def stats
|
|
151
|
+
{ readers: reader_count, write_locked: write_locked? }
|
|
152
|
+
end
|
|
153
|
+
|
|
146
154
|
def increment_readers
|
|
147
155
|
update_reader_count(1)
|
|
148
156
|
end
|
|
@@ -89,6 +89,35 @@ module Philiprehberger
|
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
# Execute a block with a file lock, retrying with exponential backoff on failure
|
|
93
|
+
#
|
|
94
|
+
# @param path [String] path to the lock file
|
|
95
|
+
# @param retries [Integer] maximum number of attempts
|
|
96
|
+
# @param delay [Numeric] initial delay in seconds between retries
|
|
97
|
+
# @param backoff [Numeric] multiplier applied to delay after each failed attempt
|
|
98
|
+
# @param timeout [Numeric, nil] seconds to wait per acquire attempt
|
|
99
|
+
# @param auto_cleanup [Boolean] automatically remove stale locks before acquiring
|
|
100
|
+
# @param ttl [Numeric, nil] time-to-live in seconds; lock expires after this duration
|
|
101
|
+
# @yield block to execute while the lock is held
|
|
102
|
+
# @return [Object] the return value of the block
|
|
103
|
+
# @raise [Error] if all retry attempts are exhausted
|
|
104
|
+
def self.with_retry_lock(path, retries: 3, delay: 0.1, backoff: 2, timeout: nil, auto_cleanup: true, ttl: nil, &block)
|
|
105
|
+
attempts = 0
|
|
106
|
+
current_delay = delay
|
|
107
|
+
|
|
108
|
+
loop do
|
|
109
|
+
attempts += 1
|
|
110
|
+
begin
|
|
111
|
+
return with_file_lock(path, timeout: timeout, auto_cleanup: auto_cleanup, ttl: ttl, &block)
|
|
112
|
+
rescue Error
|
|
113
|
+
raise if attempts >= retries
|
|
114
|
+
|
|
115
|
+
sleep current_delay
|
|
116
|
+
current_delay *= backoff
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
92
121
|
# Check if a file is currently locked by another process
|
|
93
122
|
#
|
|
94
123
|
# @param path [String] path to the lock file
|
|
@@ -97,6 +126,14 @@ module Philiprehberger
|
|
|
97
126
|
FileLock.new(path).locked?
|
|
98
127
|
end
|
|
99
128
|
|
|
129
|
+
# Snapshot of read-write lock state for `path` as `{ readers:, write_locked: }`.
|
|
130
|
+
#
|
|
131
|
+
# @param path [String] base path used with `with_read_lock` / `with_write_lock`
|
|
132
|
+
# @return [Hash] `{ readers: Integer, write_locked: Boolean }`
|
|
133
|
+
def self.rw_stats(path)
|
|
134
|
+
ReadWriteLock.new(path).stats
|
|
135
|
+
end
|
|
136
|
+
|
|
100
137
|
# Check if a PID file references a dead process
|
|
101
138
|
#
|
|
102
139
|
# @param pid_file [String] path to the PID file
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-lock_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: File locks using flock and PID file locks with stale detection for coordinating
|
|
14
14
|
between processes. Timeout support and automatic cleanup.
|