philiprehberger-task_queue 0.2.10 → 0.4.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 +18 -0
- data/README.md +62 -3
- data/lib/philiprehberger/task_queue/queue.rb +64 -3
- data/lib/philiprehberger/task_queue/version.rb +1 -1
- data/lib/philiprehberger/task_queue/worker.rb +10 -3
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a1d028dda2118c9948cbec0cdff5f7215eeb8d76615acdeaf9a2e7b5f6c45ce
|
|
4
|
+
data.tar.gz: 94d56b9b9acb20add7a8e91dd1977f1a87a135e442dadc4c0b8acfc4eb5b5ac1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c6f850324f487d516626884c44278bce4927b2419ce9b0036dd2d1c399b41a006c2ab0d48a3f18ef60a9ebcf00ef82fbf6a70e3bc486b78cdea4c397e9dc246
|
|
7
|
+
data.tar.gz: efb376ce60b1b9bfe43676ccefb5c0a2d03874e61d897337a6c0268555477478528583be36ab4763c5c0422b1e0fd4734db6bfa7dd738757a9e2f7616b8415f9
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-04-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `in_flight` count in `stats` hash for monitoring active task execution
|
|
14
|
+
- `Queue#pause` and `Queue#resume` for temporarily suspending task consumption
|
|
15
|
+
- `Queue#paused?` to check pause state
|
|
16
|
+
- `Queue#clear` to discard all pending tasks
|
|
17
|
+
|
|
18
|
+
## [0.3.0] - 2026-04-04
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `on_complete(&block)` callback that fires after each successful task completion with the task result
|
|
22
|
+
|
|
23
|
+
## [0.2.11] - 2026-03-31
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- Add GitHub issue templates, dependabot config, and PR template
|
|
27
|
+
|
|
10
28
|
## [0.2.10] - 2026-03-31
|
|
11
29
|
|
|
12
30
|
### Changed
|
data/README.md
CHANGED
|
@@ -64,12 +64,31 @@ queue.push { File.read("/nonexistent") }
|
|
|
64
64
|
|
|
65
65
|
queue.drain(timeout: 5)
|
|
66
66
|
puts queue.stats
|
|
67
|
-
# => { completed: 0, failed: 2, pending: 0 }
|
|
67
|
+
# => { completed: 0, failed: 2, pending: 0, in_flight: 0 }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Completion callback
|
|
71
|
+
|
|
72
|
+
Register a callback to run after each successful task completion. The callback receives the return value of the task.
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
queue = Philiprehberger::TaskQueue.new(concurrency: 2)
|
|
76
|
+
|
|
77
|
+
queue.on_complete do |result|
|
|
78
|
+
puts "Task finished with: #{result}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
queue.push { 42 }
|
|
82
|
+
queue.push { { status: "ok" } }
|
|
83
|
+
|
|
84
|
+
queue.drain(timeout: 5)
|
|
85
|
+
# Task finished with: 42
|
|
86
|
+
# Task finished with: {:status=>"ok"}
|
|
68
87
|
```
|
|
69
88
|
|
|
70
89
|
### Statistics
|
|
71
90
|
|
|
72
|
-
`stats` returns a snapshot of completed, failed, and
|
|
91
|
+
`stats` returns a snapshot of completed, failed, pending, and in-flight counts. All counters are thread-safe and updated atomically after each task finishes.
|
|
73
92
|
|
|
74
93
|
```ruby
|
|
75
94
|
queue = Philiprehberger::TaskQueue.new(concurrency: 4)
|
|
@@ -81,9 +100,44 @@ stats = queue.stats
|
|
|
81
100
|
puts "Completed: #{stats[:completed]}"
|
|
82
101
|
puts "Failed: #{stats[:failed]}"
|
|
83
102
|
puts "Pending: #{stats[:pending]}"
|
|
103
|
+
puts "In-flight: #{stats[:in_flight]}"
|
|
84
104
|
# Completed: 19
|
|
85
105
|
# Failed: 1
|
|
86
106
|
# Pending: 0
|
|
107
|
+
# In-flight: 0
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Pause and resume
|
|
111
|
+
|
|
112
|
+
Temporarily suspend task consumption without shutting down. In-flight tasks will finish, but no new tasks are picked up until the queue is resumed.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
queue = Philiprehberger::TaskQueue.new(concurrency: 4)
|
|
116
|
+
|
|
117
|
+
10.times { |i| queue.push { process(i) } }
|
|
118
|
+
|
|
119
|
+
queue.pause
|
|
120
|
+
puts queue.paused? # => true
|
|
121
|
+
|
|
122
|
+
# Tasks already in flight will complete, but pending tasks wait.
|
|
123
|
+
queue.resume
|
|
124
|
+
puts queue.paused? # => false
|
|
125
|
+
|
|
126
|
+
queue.shutdown(timeout: 10)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Clear pending tasks
|
|
130
|
+
|
|
131
|
+
Discard all pending tasks from the queue. Returns the number of tasks removed.
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
queue = Philiprehberger::TaskQueue.new(concurrency: 2)
|
|
135
|
+
|
|
136
|
+
100.times { |i| queue.push { process(i) } }
|
|
137
|
+
cleared = queue.clear
|
|
138
|
+
puts "Cleared #{cleared} tasks"
|
|
139
|
+
|
|
140
|
+
queue.shutdown(timeout: 5)
|
|
87
141
|
```
|
|
88
142
|
|
|
89
143
|
### FIFO ordering guarantees
|
|
@@ -140,9 +194,14 @@ queue.shutdown(timeout: 5)
|
|
|
140
194
|
| `#size` | _(none)_ | `Integer` | Number of pending (not yet started) tasks |
|
|
141
195
|
| `#running?` | _(none)_ | `Boolean` | Whether the queue is accepting new tasks |
|
|
142
196
|
| `#shutdown(timeout:)` | `timeout` — seconds to wait for workers (Numeric, default `30`) | `nil` | Signal workers to stop, drain remaining tasks, join threads up to `timeout` seconds |
|
|
197
|
+
| `#on_complete(&block)` | `&block` — callback receiving `(result)` | `self` | Register a callback invoked after each successful task completion with the task's return value |
|
|
143
198
|
| `#on_error(&block)` | `&block` — callback receiving `(exception, task)` | `self` | Register an error callback invoked when a task raises a `StandardError` |
|
|
144
|
-
| `#stats` | _(none)_ | `Hash` | Returns `{ completed:, failed:, pending: }` with Integer counts |
|
|
199
|
+
| `#stats` | _(none)_ | `Hash` | Returns `{ completed:, failed:, pending:, in_flight: }` with Integer counts |
|
|
145
200
|
| `#drain(timeout:)` | `timeout` — seconds to wait (Numeric, default `30`) | `nil` | Block until all pending and in-flight tasks complete without shutting down |
|
|
201
|
+
| `#pause` | _(none)_ | `self` | Suspend task consumption; in-flight tasks finish but no new tasks are picked up |
|
|
202
|
+
| `#resume` | _(none)_ | `self` | Resume a paused queue, waking workers to continue processing |
|
|
203
|
+
| `#paused?` | _(none)_ | `Boolean` | Whether the queue is currently paused |
|
|
204
|
+
| `#clear` | _(none)_ | `Integer` | Remove all pending tasks and return the number cleared |
|
|
146
205
|
|
|
147
206
|
## Development
|
|
148
207
|
|
|
@@ -19,7 +19,10 @@ module Philiprehberger
|
|
|
19
19
|
@workers = []
|
|
20
20
|
@running = true
|
|
21
21
|
@started = false
|
|
22
|
+
@paused = false
|
|
23
|
+
@pause_condition = ConditionVariable.new
|
|
22
24
|
@error_handler = nil
|
|
25
|
+
@complete_handler = nil
|
|
23
26
|
@stats = { completed: 0, failed: 0, in_flight: 0 }
|
|
24
27
|
end
|
|
25
28
|
|
|
@@ -34,12 +37,66 @@ module Philiprehberger
|
|
|
34
37
|
self
|
|
35
38
|
end
|
|
36
39
|
|
|
40
|
+
# Register a callback invoked after each successful task completion.
|
|
41
|
+
#
|
|
42
|
+
# The callback receives the return value of the completed task.
|
|
43
|
+
#
|
|
44
|
+
# @yield [result] called on task success
|
|
45
|
+
# @return [self]
|
|
46
|
+
def on_complete(&block)
|
|
47
|
+
@mutex.synchronize { @complete_handler = block }
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
37
51
|
# Return statistics about processed tasks.
|
|
38
52
|
#
|
|
39
|
-
# @return [Hash{Symbol => Integer}] counts for :completed, :failed, :pending
|
|
53
|
+
# @return [Hash{Symbol => Integer}] counts for :completed, :failed, :pending, :in_flight
|
|
40
54
|
def stats
|
|
41
55
|
@mutex.synchronize do
|
|
42
|
-
{ completed: @stats[:completed], failed: @stats[:failed], pending: @tasks.size
|
|
56
|
+
{ completed: @stats[:completed], failed: @stats[:failed], pending: @tasks.size,
|
|
57
|
+
in_flight: @stats[:in_flight] }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Pause the queue so workers stop dequeuing new tasks.
|
|
62
|
+
#
|
|
63
|
+
# In-flight tasks will finish, but no new tasks will be picked up until
|
|
64
|
+
# +resume+ is called.
|
|
65
|
+
#
|
|
66
|
+
# @return [self]
|
|
67
|
+
def pause
|
|
68
|
+
@mutex.synchronize do
|
|
69
|
+
@paused = true
|
|
70
|
+
end
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Resume a paused queue, waking workers to continue processing.
|
|
75
|
+
#
|
|
76
|
+
# @return [self]
|
|
77
|
+
def resume
|
|
78
|
+
@mutex.synchronize do
|
|
79
|
+
@paused = false
|
|
80
|
+
@pause_condition.broadcast
|
|
81
|
+
end
|
|
82
|
+
self
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Whether the queue is currently paused.
|
|
86
|
+
#
|
|
87
|
+
# @return [Boolean]
|
|
88
|
+
def paused?
|
|
89
|
+
@mutex.synchronize { @paused }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Remove all pending tasks from the queue.
|
|
93
|
+
#
|
|
94
|
+
# @return [Integer] number of tasks cleared
|
|
95
|
+
def clear
|
|
96
|
+
@mutex.synchronize do
|
|
97
|
+
count = @tasks.size
|
|
98
|
+
@tasks.clear
|
|
99
|
+
count
|
|
43
100
|
end
|
|
44
101
|
end
|
|
45
102
|
|
|
@@ -116,8 +173,10 @@ module Philiprehberger
|
|
|
116
173
|
return unless @running
|
|
117
174
|
|
|
118
175
|
@running = false
|
|
176
|
+
@paused = false
|
|
119
177
|
@workers.each(&:stop)
|
|
120
178
|
@condition.broadcast
|
|
179
|
+
@pause_condition.broadcast
|
|
121
180
|
end
|
|
122
181
|
end
|
|
123
182
|
|
|
@@ -133,7 +192,9 @@ module Philiprehberger
|
|
|
133
192
|
@concurrency.times do
|
|
134
193
|
@workers << Worker.new(
|
|
135
194
|
@tasks, @mutex, @condition,
|
|
136
|
-
context: { stats: @stats, error_handler: @error_handler,
|
|
195
|
+
context: { stats: @stats, error_handler: @error_handler,
|
|
196
|
+
complete_handler: @complete_handler, drain_condition: @drain_condition,
|
|
197
|
+
paused: -> { @paused }, pause_condition: @pause_condition }
|
|
137
198
|
)
|
|
138
199
|
end
|
|
139
200
|
@started = true
|
|
@@ -12,7 +12,10 @@ module Philiprehberger
|
|
|
12
12
|
@condition = condition
|
|
13
13
|
@stats = context[:stats]
|
|
14
14
|
@error_handler = context[:error_handler]
|
|
15
|
+
@complete_handler = context[:complete_handler]
|
|
15
16
|
@drain_condition = context[:drain_condition]
|
|
17
|
+
@paused = context[:paused]
|
|
18
|
+
@pause_condition = context[:pause_condition]
|
|
16
19
|
@running = true
|
|
17
20
|
@thread = Thread.new { run }
|
|
18
21
|
end
|
|
@@ -42,23 +45,27 @@ module Philiprehberger
|
|
|
42
45
|
@condition.wait(@mutex) while @queue.empty? && @running
|
|
43
46
|
return nil unless @running || !@queue.empty?
|
|
44
47
|
|
|
48
|
+
@pause_condition.wait(@mutex) while @paused&.call && @running
|
|
49
|
+
return nil unless @running || !@queue.empty?
|
|
50
|
+
|
|
45
51
|
@stats[:in_flight] += 1
|
|
46
52
|
@queue.shift
|
|
47
53
|
end
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
def execute(task)
|
|
51
|
-
task.call
|
|
52
|
-
record_completion
|
|
57
|
+
result = task.call
|
|
58
|
+
record_completion(result)
|
|
53
59
|
rescue StandardError => e
|
|
54
60
|
record_failure(e, task)
|
|
55
61
|
end
|
|
56
62
|
|
|
57
|
-
def record_completion
|
|
63
|
+
def record_completion(result)
|
|
58
64
|
@mutex.synchronize do
|
|
59
65
|
@stats[:completed] += 1
|
|
60
66
|
@stats[:in_flight] -= 1
|
|
61
67
|
end
|
|
68
|
+
@complete_handler&.call(result)
|
|
62
69
|
end
|
|
63
70
|
|
|
64
71
|
def record_failure(error, task)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-task_queue
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.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-
|
|
11
|
+
date: 2026-04-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A lightweight, zero-dependency, thread-safe in-process async job queue
|
|
14
14
|
with configurable concurrency for Ruby applications.
|
|
@@ -25,11 +25,11 @@ files:
|
|
|
25
25
|
- lib/philiprehberger/task_queue/queue.rb
|
|
26
26
|
- lib/philiprehberger/task_queue/version.rb
|
|
27
27
|
- lib/philiprehberger/task_queue/worker.rb
|
|
28
|
-
homepage: https://
|
|
28
|
+
homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-task_queue
|
|
29
29
|
licenses:
|
|
30
30
|
- MIT
|
|
31
31
|
metadata:
|
|
32
|
-
homepage_uri: https://
|
|
32
|
+
homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-task_queue
|
|
33
33
|
source_code_uri: https://github.com/philiprehberger/rb-task-queue
|
|
34
34
|
changelog_uri: https://github.com/philiprehberger/rb-task-queue/blob/main/CHANGELOG.md
|
|
35
35
|
bug_tracker_uri: https://github.com/philiprehberger/rb-task-queue/issues
|