philiprehberger-retry_queue 0.2.1 → 0.3.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 +5 -0
- data/README.md +16 -1
- data/lib/philiprehberger/retry_queue/processor.rb +16 -1
- data/lib/philiprehberger/retry_queue/version.rb +1 -1
- data/lib/philiprehberger/retry_queue.rb +7 -2
- 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: a5c95df7fb98070ed959408d5fce1be2d1b920a800221d831f24214ffe9425d1
|
|
4
|
+
data.tar.gz: d681958fb8eddf3710283315eb52ff81cc13bec03d67c0e72d1199ec5025ec9e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b91743778fba30ed468fe23da3157c4df3086a81c5aa1784f5d304a967967ad271e7dd9646a54d75a54379b5123f8589614249c346fa760ccc05111f7e6bdcc
|
|
7
|
+
data.tar.gz: e63ef43720e3915d2942c06fd34e33ca79aabe147b3fc678db7812759c0501e6b660e80e4ae37afcc59ab031f8250e04371766888cd873e2edd7f1dc1f9ae0bf
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-04-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `on_failure:` callback for `process` and `Processor`, invoked with `(item, error)` when an item exhausts retries; hook exceptions are swallowed
|
|
14
|
+
|
|
10
15
|
## [0.2.1] - 2026-03-31
|
|
11
16
|
|
|
12
17
|
### Changed
|
data/README.md
CHANGED
|
@@ -67,6 +67,20 @@ result = Philiprehberger::RetryQueue.process(items, max_retries: 3, on_retry: [l
|
|
|
67
67
|
end
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
### Dead-letter Notifications
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
on_failure = ->(item, error) { Rails.logger.error("Dead-lettered #{item}: #{error.message}") }
|
|
74
|
+
|
|
75
|
+
result = Philiprehberger::RetryQueue.process(items, max_retries: 3, on_failure: on_failure) do |item|
|
|
76
|
+
process_item(item)
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The hook fires once per item that exhausts its retries, just as the item is recorded in
|
|
81
|
+
`Result#failed`. Exceptions raised inside the hook are swallowed so a faulty callback cannot
|
|
82
|
+
break the queue.
|
|
83
|
+
|
|
70
84
|
### DLQ Reprocessing
|
|
71
85
|
|
|
72
86
|
```ruby
|
|
@@ -97,7 +111,8 @@ stats = result.stats
|
|
|
97
111
|
|
|
98
112
|
| Method | Description |
|
|
99
113
|
|--------|-------------|
|
|
100
|
-
| `.process(items, max_retries:, concurrency:, backoff:, retry_on:, on_retry:) { \|item\| }` | Process items with retry logic |
|
|
114
|
+
| `.process(items, max_retries:, concurrency:, backoff:, retry_on:, on_retry:, on_failure:) { \|item\| }` | Process items with retry logic |
|
|
115
|
+
| `on_failure:` | Callable `(item, error)` invoked once per item that exhausts retries; hook errors are swallowed |
|
|
101
116
|
| `Result#succeeded` | Array of successfully processed items |
|
|
102
117
|
| `Result#failed` | Array of hashes with `:item`, `:error`, `:attempts` |
|
|
103
118
|
| `Result#stats` | Hash with `:total`, `:succeeded`, `:failed`, `:success_rate`, `:elapsed` |
|
|
@@ -12,7 +12,10 @@ module Philiprehberger
|
|
|
12
12
|
# @param backoff [Proc, nil] proc receiving attempt number, returns sleep duration
|
|
13
13
|
# @param retry_on [Array<Class>, nil] exception classes to retry on; nil means retry all
|
|
14
14
|
# @param on_retry [Array<Proc>, Proc, nil] callbacks fired before each retry attempt
|
|
15
|
-
|
|
15
|
+
# @param on_failure [Proc, nil] callable invoked with `(item, error)` once per item that
|
|
16
|
+
# exhausts retries and moves to the dead-letter list; exceptions raised by the hook are
|
|
17
|
+
# swallowed so a faulty hook cannot break the queue
|
|
18
|
+
def initialize(max_retries: 3, concurrency: 1, backoff: nil, retry_on: nil, on_retry: nil, on_failure: nil)
|
|
16
19
|
raise Error, 'max_retries must be non-negative' unless max_retries.is_a?(Integer) && max_retries >= 0
|
|
17
20
|
|
|
18
21
|
@max_retries = max_retries
|
|
@@ -20,6 +23,7 @@ module Philiprehberger
|
|
|
20
23
|
@backoff = backoff || DEFAULT_BACKOFF
|
|
21
24
|
@retry_on = retry_on
|
|
22
25
|
@on_retry_hooks = Array(on_retry)
|
|
26
|
+
@on_failure = on_failure
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
# Process a collection of items with retry logic.
|
|
@@ -54,6 +58,7 @@ module Philiprehberger
|
|
|
54
58
|
rescue StandardError => e
|
|
55
59
|
if !retryable?(e) || attempts > @max_retries
|
|
56
60
|
failed << { item: item, error: e, attempts: attempts }
|
|
61
|
+
fire_on_failure_hook(item, e)
|
|
57
62
|
return
|
|
58
63
|
end
|
|
59
64
|
|
|
@@ -74,6 +79,16 @@ module Philiprehberger
|
|
|
74
79
|
@on_retry_hooks.each { |hook| hook.call(item, error, attempt) }
|
|
75
80
|
end
|
|
76
81
|
|
|
82
|
+
def fire_on_failure_hook(item, error)
|
|
83
|
+
return if @on_failure.nil?
|
|
84
|
+
|
|
85
|
+
@on_failure.call(item, error)
|
|
86
|
+
rescue StandardError
|
|
87
|
+
# Swallow hook errors — this is a best-effort notification and must never
|
|
88
|
+
# break the queue. Intentionally silent; users can log inside their hook.
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
77
92
|
def now
|
|
78
93
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
79
94
|
end
|
|
@@ -16,15 +16,20 @@ module Philiprehberger
|
|
|
16
16
|
# @param backoff [Proc, nil] custom backoff strategy
|
|
17
17
|
# @param retry_on [Array<Class>, nil] exception classes to retry on; others go straight to failed
|
|
18
18
|
# @param on_retry [Array<Proc>, nil] callbacks fired before each retry attempt
|
|
19
|
+
# @param on_failure [Proc, nil] callable invoked with `(item, error)` once per item that
|
|
20
|
+
# exhausts retries and moves to the dead-letter list; exceptions raised by the hook are
|
|
21
|
+
# swallowed so a faulty hook cannot break the queue
|
|
19
22
|
# @yield [item] block that processes a single item
|
|
20
23
|
# @return [Result] processing result
|
|
21
|
-
def self.process(items, max_retries: 3, concurrency: 1, backoff: nil, retry_on: nil, on_retry: nil,
|
|
24
|
+
def self.process(items, max_retries: 3, concurrency: 1, backoff: nil, retry_on: nil, on_retry: nil,
|
|
25
|
+
on_failure: nil, &block)
|
|
22
26
|
processor = Processor.new(
|
|
23
27
|
max_retries: max_retries,
|
|
24
28
|
concurrency: concurrency,
|
|
25
29
|
backoff: backoff,
|
|
26
30
|
retry_on: retry_on,
|
|
27
|
-
on_retry: on_retry
|
|
31
|
+
on_retry: on_retry,
|
|
32
|
+
on_failure: on_failure
|
|
28
33
|
)
|
|
29
34
|
processor.call(items, &block)
|
|
30
35
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-retry_queue
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.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-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Processes collections of items with configurable per-item retry logic,
|
|
14
14
|
exponential backoff, and dead letter collection for failed items. Returns detailed
|
|
@@ -26,11 +26,11 @@ files:
|
|
|
26
26
|
- lib/philiprehberger/retry_queue/processor.rb
|
|
27
27
|
- lib/philiprehberger/retry_queue/result.rb
|
|
28
28
|
- lib/philiprehberger/retry_queue/version.rb
|
|
29
|
-
homepage: https://
|
|
29
|
+
homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-retry_queue
|
|
30
30
|
licenses:
|
|
31
31
|
- MIT
|
|
32
32
|
metadata:
|
|
33
|
-
homepage_uri: https://
|
|
33
|
+
homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-retry_queue
|
|
34
34
|
source_code_uri: https://github.com/philiprehberger/rb-retry-queue
|
|
35
35
|
changelog_uri: https://github.com/philiprehberger/rb-retry-queue/blob/main/CHANGELOG.md
|
|
36
36
|
bug_tracker_uri: https://github.com/philiprehberger/rb-retry-queue/issues
|