event_people 1.2.0 → 1.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccaafb3d62e2c63881bfc08b080e4f129d79911a6f5d1a1e2aa3929a2b719bff
4
- data.tar.gz: 889f25c2629daa9ee21a6c3621435ffc0eaae30c0ea9335bbf94da82eec126b8
3
+ metadata.gz: c7f60b5a8806b69e26b0f66ea3a547e92200f460df4f30a2df90eed2fa08641d
4
+ data.tar.gz: 343c7a4e19f4a0194e552b507f3d000a1fd723faf374a35faee8961527c1809f
5
5
  SHA512:
6
- metadata.gz: b615ef14921b27781edc64e48bb637965e1fb91d99995ec231a0b731b5f21bcdb73c8001528646e051c52d1ab86c6f70a92c8250521b668d983bfb52a805adcc
7
- data.tar.gz: 33628df7e0a91b4247724173e354117cb28f39e83bdd8bd0711df2265b632c183b8185c8fabdc290c24dcab4f55e085761a846720ab8b12fd89b4f90fd17da8f
6
+ metadata.gz: dbd234e3c4df60f769feb130c05b8d96421677e8e297704b5620f41749ba3a507bfae2d22af9c020612d5fbb2b1f426bc427b8e71f476235de68a1fb806e1e38
7
+ data.tar.gz: 791f1ee04535ed6eef1d3fd9cb5771da399b336b946d9b4fef7f46b43d362781c6698a26aafac727620ef792f93a2f8fab6f2e59f3460655073c8a2937ab8600
data/.event_people.yml CHANGED
@@ -1,5 +1,5 @@
1
- spec_version: "1.1.0"
2
- implementation_version: "1.2.0"
1
+ spec_version: "1.1.1"
2
+ implementation_version: "1.2.1"
3
3
  language: ruby
4
4
  package: "event_people (RubyGems)"
5
5
  status: stable
data/README.md CHANGED
@@ -220,6 +220,65 @@ EventPeople::Daemon.start
220
220
  ```
221
221
  [See more details](https://github.com/pin-people/event_people_ruby/blob/master/examples/daemon.rb)
222
222
 
223
+ ## Retry and Dead Letter Queue (DLQ)
224
+
225
+ ### Environment variables
226
+
227
+ | Variable | Description | Default |
228
+ |---|---|---|
229
+ | `RABBIT_EVENT_PEOPLE_MAX_RETRIES` | Max retry attempts before dead-lettering | `3` |
230
+ | `RABBIT_EVENT_PEOPLE_RETRY_TTL_MS` | Base delay in ms for retry backoff | `1000` |
231
+
232
+ ### How it works
233
+
234
+ On `context.fail`:
235
+ - If retries remain → message published to `{queue}_retry` with exponential backoff delay, then acked
236
+ - If retries exhausted → nacked to DLQ via RabbitMQ DLX
237
+
238
+ On `context.reject` → nacked directly to DLQ (no retries)
239
+
240
+ **Delay strategies:**
241
+ - `exponential` (default): `min(initialDelay × 5^retry_count, 600000)` ms
242
+ - `fixed`: constant `initialDelay` ms
243
+
244
+ ### Queue topology (auto-created on subscribe)
245
+
246
+ | Queue/Exchange | Name | Purpose |
247
+ |---|---|---|
248
+ | Exchange (DLX) | `{app_name}_dlx` | Fanout, receives dead-lettered messages |
249
+ | DLQ | `{app_name}_dlq` | Final resting place for failed messages |
250
+ | Retry queue | `{queue_name}_retry` | Holds messages until backoff delay expires |
251
+
252
+ ### Usage
253
+
254
+ ```ruby
255
+ class OrderListener < EventPeople::Listeners::Base
256
+ bind :handle_created, 'order.service.created'
257
+
258
+ def handle_created(event)
259
+ puts "Attempt #{event.retry_count + 1} of #{context.max_retries}"
260
+
261
+ if invalid?(event)
262
+ reject # → DLQ immediately, no retries
263
+ return
264
+ end
265
+
266
+ process(event)
267
+ success
268
+ rescue StandardError
269
+ puts 'Final attempt, sending to DLQ' if context.is_last_retry
270
+ fail # → retry queue (or DLQ if exhausted)
271
+ end
272
+ end
273
+
274
+ # Per-listener retry config (overrides env var defaults)
275
+ EventPeople::Listener.on('order.service.created', method(:handle),
276
+ max_attempts: 5,
277
+ delay_strategy: 'exponential')
278
+ ```
279
+
280
+ > **Note:** `success!`, `fail!`, `reject!` still work but are deprecated — prefer `success`, `fail`, `reject`.
281
+
223
282
  ## Development
224
283
 
225
284
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -34,18 +34,23 @@ module EventPeople
34
34
  expiration: delay.to_s,
35
35
  headers: { 'x-event-people-retries' => @retry_count + 1 }
36
36
  )
37
- @channel.ack(@delivery_info.delivery_tag, false)
38
37
  rescue => e
39
- # If publish+ack fails, nack so the message is redelivered from the main queue.
40
- # This risks duplication if publish succeeded but ack failed, which is an inherent
41
- # AMQP at-least-once limitation. We prefer redelivery over silent loss.
38
+ # Publish failed nack without requeue so the DLX routes to DLQ.
39
+ # Requeuing without incrementing x-event-people-retries would cause an infinite loop.
42
40
  begin
43
- @channel.nack(@delivery_info.delivery_tag, false, true)
41
+ @channel.nack(@delivery_info.delivery_tag, false, false)
44
42
  rescue
45
43
  # Channel may already be closed; nothing we can do.
46
44
  end
47
45
  raise e
48
46
  end
47
+ begin
48
+ @channel.ack(@delivery_info.delivery_tag, false)
49
+ rescue
50
+ # Publish already succeeded; swallow ack errors. The message may be redelivered
51
+ # once (at-least-once), but that is safer than nacking to DLQ when a retry copy
52
+ # is already enqueued.
53
+ end
49
54
  else
50
55
  @channel.nack(@delivery_info.delivery_tag, false, false)
51
56
  end
@@ -1,3 +1,3 @@
1
1
  module EventPeople
2
- VERSION = '1.2.0'
2
+ VERSION = '1.2.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_people
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pin People
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-12 00:00:00.000000000 Z
11
+ date: 2026-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler