philiprehberger-task_queue 0.3.0 → 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 +8 -0
- data/README.md +42 -3
- data/lib/philiprehberger/task_queue/queue.rb +51 -3
- data/lib/philiprehberger/task_queue/version.rb +1 -1
- data/lib/philiprehberger/task_queue/worker.rb +5 -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: 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,14 @@ 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
|
+
|
|
10
18
|
## [0.3.0] - 2026-04-04
|
|
11
19
|
|
|
12
20
|
### Added
|
data/README.md
CHANGED
|
@@ -64,7 +64,7 @@ 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
68
|
```
|
|
69
69
|
|
|
70
70
|
### Completion callback
|
|
@@ -88,7 +88,7 @@ queue.drain(timeout: 5)
|
|
|
88
88
|
|
|
89
89
|
### Statistics
|
|
90
90
|
|
|
91
|
-
`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.
|
|
92
92
|
|
|
93
93
|
```ruby
|
|
94
94
|
queue = Philiprehberger::TaskQueue.new(concurrency: 4)
|
|
@@ -100,9 +100,44 @@ stats = queue.stats
|
|
|
100
100
|
puts "Completed: #{stats[:completed]}"
|
|
101
101
|
puts "Failed: #{stats[:failed]}"
|
|
102
102
|
puts "Pending: #{stats[:pending]}"
|
|
103
|
+
puts "In-flight: #{stats[:in_flight]}"
|
|
103
104
|
# Completed: 19
|
|
104
105
|
# Failed: 1
|
|
105
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)
|
|
106
141
|
```
|
|
107
142
|
|
|
108
143
|
### FIFO ordering guarantees
|
|
@@ -161,8 +196,12 @@ queue.shutdown(timeout: 5)
|
|
|
161
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 |
|
|
162
197
|
| `#on_complete(&block)` | `&block` — callback receiving `(result)` | `self` | Register a callback invoked after each successful task completion with the task's return value |
|
|
163
198
|
| `#on_error(&block)` | `&block` — callback receiving `(exception, task)` | `self` | Register an error callback invoked when a task raises a `StandardError` |
|
|
164
|
-
| `#stats` | _(none)_ | `Hash` | Returns `{ completed:, failed:, pending: }` with Integer counts |
|
|
199
|
+
| `#stats` | _(none)_ | `Hash` | Returns `{ completed:, failed:, pending:, in_flight: }` with Integer counts |
|
|
165
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 |
|
|
166
205
|
|
|
167
206
|
## Development
|
|
168
207
|
|
|
@@ -19,6 +19,8 @@ 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
|
|
23
25
|
@complete_handler = nil
|
|
24
26
|
@stats = { completed: 0, failed: 0, in_flight: 0 }
|
|
@@ -48,10 +50,53 @@ module Philiprehberger
|
|
|
48
50
|
|
|
49
51
|
# Return statistics about processed tasks.
|
|
50
52
|
#
|
|
51
|
-
# @return [Hash{Symbol => Integer}] counts for :completed, :failed, :pending
|
|
53
|
+
# @return [Hash{Symbol => Integer}] counts for :completed, :failed, :pending, :in_flight
|
|
52
54
|
def stats
|
|
53
55
|
@mutex.synchronize do
|
|
54
|
-
{ 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
|
|
55
100
|
end
|
|
56
101
|
end
|
|
57
102
|
|
|
@@ -128,8 +173,10 @@ module Philiprehberger
|
|
|
128
173
|
return unless @running
|
|
129
174
|
|
|
130
175
|
@running = false
|
|
176
|
+
@paused = false
|
|
131
177
|
@workers.each(&:stop)
|
|
132
178
|
@condition.broadcast
|
|
179
|
+
@pause_condition.broadcast
|
|
133
180
|
end
|
|
134
181
|
end
|
|
135
182
|
|
|
@@ -146,7 +193,8 @@ module Philiprehberger
|
|
|
146
193
|
@workers << Worker.new(
|
|
147
194
|
@tasks, @mutex, @condition,
|
|
148
195
|
context: { stats: @stats, error_handler: @error_handler,
|
|
149
|
-
complete_handler: @complete_handler, drain_condition: @drain_condition
|
|
196
|
+
complete_handler: @complete_handler, drain_condition: @drain_condition,
|
|
197
|
+
paused: -> { @paused }, pause_condition: @pause_condition }
|
|
150
198
|
)
|
|
151
199
|
end
|
|
152
200
|
@started = true
|
|
@@ -14,6 +14,8 @@ module Philiprehberger
|
|
|
14
14
|
@error_handler = context[:error_handler]
|
|
15
15
|
@complete_handler = context[:complete_handler]
|
|
16
16
|
@drain_condition = context[:drain_condition]
|
|
17
|
+
@paused = context[:paused]
|
|
18
|
+
@pause_condition = context[:pause_condition]
|
|
17
19
|
@running = true
|
|
18
20
|
@thread = Thread.new { run }
|
|
19
21
|
end
|
|
@@ -43,6 +45,9 @@ module Philiprehberger
|
|
|
43
45
|
@condition.wait(@mutex) while @queue.empty? && @running
|
|
44
46
|
return nil unless @running || !@queue.empty?
|
|
45
47
|
|
|
48
|
+
@pause_condition.wait(@mutex) while @paused&.call && @running
|
|
49
|
+
return nil unless @running || !@queue.empty?
|
|
50
|
+
|
|
46
51
|
@stats[:in_flight] += 1
|
|
47
52
|
@queue.shift
|
|
48
53
|
end
|
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-04-
|
|
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.
|