legionio 1.8.1 → 1.8.2
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 +7 -0
- data/lib/legion/extensions/actors/retry_policy.rb +38 -0
- data/lib/legion/extensions/actors/subscription.rb +45 -3
- data/lib/legion/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 274f2876b6e344eba4a13f69464f8d2314184ac72b421758d8fbc54b40934fa7
|
|
4
|
+
data.tar.gz: 7fa9c97c435509b18b2f68cdc2a041f496b613a7cf163b053c5d286a2ae9870a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64226b609440433ddf6db4bcfaf102318e50d22ed6a117ddf113086fb85988d4df4dfecd1d162c20d5e5b1d1b5e4a69dbc8a426b5b5a05e6f9a83fb7e4add5f8
|
|
7
|
+
data.tar.gz: 4ddd4175e2584e249e6e74e4101d6ed3ae15be7d60b3051e9025f8f8bb286e84cba73e00a1715443abd1cae216249b7328aed9daeb66ed8558282385f618dcf5
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.8.2] - 2026-04-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `Legion::Extensions::Actors::RetryPolicy` — configurable retry threshold module with `should_retry?`, `extract_retry_count`, and `retry_threshold` helpers
|
|
9
|
+
- Subscription actor `reject_or_retry` — counts retries via `x-retry-count` header, republishes with incremented header and exponential backoff (`2^n * base_delay`, capped at `max_delay`), dead-letters to DLX when threshold exceeded
|
|
10
|
+
- Settings: `fleet.poison_message_threshold` (primary), `transport.retry_threshold` (fallback), `fleet.transport.retry_base_delay_seconds`, `fleet.transport.retry_max_delay_seconds`
|
|
11
|
+
|
|
5
12
|
## [1.7.37] - 2026-04-09
|
|
6
13
|
|
|
7
14
|
### Added
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Actors
|
|
6
|
+
module RetryPolicy
|
|
7
|
+
DEFAULT_THRESHOLD = 2
|
|
8
|
+
RETRY_COUNT_HEADER = 'x-retry-count'
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def should_retry?(retry_count:, threshold:)
|
|
13
|
+
return true if threshold.nil?
|
|
14
|
+
|
|
15
|
+
retry_count < threshold
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def extract_retry_count(headers)
|
|
19
|
+
return 0 if headers.nil?
|
|
20
|
+
|
|
21
|
+
count = headers[RETRY_COUNT_HEADER] || headers[RETRY_COUNT_HEADER.to_sym] || 0
|
|
22
|
+
count.to_i
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def retry_threshold
|
|
26
|
+
threshold = nil
|
|
27
|
+
if defined?(Legion::Settings)
|
|
28
|
+
threshold = Legion::Settings.dig(:fleet, :poison_message_threshold)
|
|
29
|
+
threshold ||= Legion::Settings.dig(:transport, :retry_threshold)
|
|
30
|
+
end
|
|
31
|
+
threshold || DEFAULT_THRESHOLD
|
|
32
|
+
rescue StandardError
|
|
33
|
+
DEFAULT_THRESHOLD
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'base'
|
|
4
4
|
require_relative 'dsl'
|
|
5
|
+
require_relative 'retry_policy'
|
|
5
6
|
require 'date'
|
|
6
7
|
require 'securerandom'
|
|
7
8
|
|
|
@@ -84,7 +85,7 @@ module Legion
|
|
|
84
85
|
cancel if Legion::Settings[:client][:shutting_down]
|
|
85
86
|
rescue StandardError => e
|
|
86
87
|
handle_exception(e, lex: lex_name, fn: fn, routing_key: delivery_info.routing_key)
|
|
87
|
-
|
|
88
|
+
reject_or_retry(delivery_info, metadata, payload) if manual_ack
|
|
88
89
|
end
|
|
89
90
|
log.info "[Subscription] prepared: #{lex_name}/#{runner_name}"
|
|
90
91
|
rescue StandardError => e
|
|
@@ -176,8 +177,8 @@ module Legion
|
|
|
176
177
|
cancel if Legion::Settings[:client][:shutting_down]
|
|
177
178
|
rescue StandardError => e
|
|
178
179
|
handle_exception(e)
|
|
179
|
-
log.warn "[Subscription]
|
|
180
|
-
|
|
180
|
+
log.warn "[Subscription] retry-or-dlq for #{lex_name}/#{fn}"
|
|
181
|
+
reject_or_retry(delivery_info, metadata, payload) if manual_ack
|
|
181
182
|
end
|
|
182
183
|
log.info "[Subscription] subscribed: #{lex_name}/#{runner_name} (consumer registered)" if defined?(log)
|
|
183
184
|
end
|
|
@@ -223,6 +224,47 @@ module Legion
|
|
|
223
224
|
run_block.call
|
|
224
225
|
end
|
|
225
226
|
end
|
|
227
|
+
|
|
228
|
+
def reject_or_retry(delivery_info, metadata, payload)
|
|
229
|
+
headers = metadata&.headers || {}
|
|
230
|
+
retry_count = RetryPolicy.extract_retry_count(headers)
|
|
231
|
+
threshold = RetryPolicy.retry_threshold
|
|
232
|
+
|
|
233
|
+
if RetryPolicy.should_retry?(retry_count: retry_count, threshold: threshold)
|
|
234
|
+
base_delay = Legion::Settings.dig(:fleet, :transport, :retry_base_delay_seconds) || 1
|
|
235
|
+
max_delay = Legion::Settings.dig(:fleet, :transport, :retry_max_delay_seconds) || 30
|
|
236
|
+
delay = [base_delay * (2**retry_count), max_delay].min
|
|
237
|
+
log.info "[Subscription] retrying message in #{delay}s (attempt #{retry_count + 1}/#{threshold}) for #{lex_name}"
|
|
238
|
+
sleep(delay)
|
|
239
|
+
if republish_with_retry_count(delivery_info, metadata, payload, retry_count + 1)
|
|
240
|
+
@queue.acknowledge(delivery_info.delivery_tag)
|
|
241
|
+
else
|
|
242
|
+
@queue.reject(delivery_info.delivery_tag, requeue: false)
|
|
243
|
+
end
|
|
244
|
+
else
|
|
245
|
+
log.warn "[Subscription] dead-lettering message after #{retry_count} retries for #{lex_name}"
|
|
246
|
+
@queue.reject(delivery_info.delivery_tag, requeue: false)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def republish_with_retry_count(_delivery_info, metadata, payload, new_count)
|
|
251
|
+
headers = (metadata&.headers || {}).dup
|
|
252
|
+
headers[RetryPolicy::RETRY_COUNT_HEADER] = new_count
|
|
253
|
+
|
|
254
|
+
exchange = @queue.channel.default_exchange
|
|
255
|
+
exchange.publish(
|
|
256
|
+
payload,
|
|
257
|
+
routing_key: @queue.name,
|
|
258
|
+
headers: headers,
|
|
259
|
+
content_type: metadata&.content_type,
|
|
260
|
+
content_encoding: metadata&.content_encoding,
|
|
261
|
+
persistent: true
|
|
262
|
+
)
|
|
263
|
+
true
|
|
264
|
+
rescue StandardError => e
|
|
265
|
+
log.warn "[Subscription] republish failed, dead-lettering: #{e.message}"
|
|
266
|
+
false
|
|
267
|
+
end
|
|
226
268
|
end
|
|
227
269
|
end
|
|
228
270
|
end
|
data/lib/legion/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legionio
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.8.
|
|
4
|
+
version: 1.8.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -809,6 +809,7 @@ files:
|
|
|
809
809
|
- lib/legion/extensions/actors/nothing.rb
|
|
810
810
|
- lib/legion/extensions/actors/once.rb
|
|
811
811
|
- lib/legion/extensions/actors/poll.rb
|
|
812
|
+
- lib/legion/extensions/actors/retry_policy.rb
|
|
812
813
|
- lib/legion/extensions/actors/singleton.rb
|
|
813
814
|
- lib/legion/extensions/actors/subscription.rb
|
|
814
815
|
- lib/legion/extensions/builders/absorbers.rb
|