pgbus 0.14.2 → 0.15.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +33 -0
- data/Rakefile +6 -1
- data/app/helpers/pgbus/application_helper.rb +7 -0
- data/app/views/pgbus/dead_letter/show.html.erb +17 -0
- data/app/views/pgbus/events/_pending_table.html.erb +21 -0
- data/app/views/pgbus/jobs/show.html.erb +17 -0
- data/config/locales/da.yml +3 -0
- data/config/locales/de.yml +3 -0
- data/config/locales/en.yml +3 -0
- data/config/locales/es.yml +3 -0
- data/config/locales/fi.yml +3 -0
- data/config/locales/fr.yml +3 -0
- data/config/locales/it.yml +3 -0
- data/config/locales/ja.yml +3 -0
- data/config/locales/nb.yml +3 -0
- data/config/locales/nl.yml +3 -0
- data/config/locales/pt.yml +3 -0
- data/config/locales/sv.yml +3 -0
- data/lib/pgbus/active_job/adapter.rb +5 -1
- data/lib/pgbus/active_job/current_attributes.rb +52 -0
- data/lib/pgbus/client/fair_read.rb +187 -0
- data/lib/pgbus/client.rb +30 -3
- data/lib/pgbus/configuration.rb +57 -0
- data/lib/pgbus/current_attributes.rb +180 -0
- data/lib/pgbus/engine.rb +1 -0
- data/lib/pgbus/event.rb +7 -2
- data/lib/pgbus/event_bus/handler.rb +12 -2
- data/lib/pgbus/event_bus/publisher.rb +37 -2
- data/lib/pgbus/event_bus/subscriber.rb +4 -0
- data/lib/pgbus/fair_share.rb +110 -0
- data/lib/pgbus/outbox.rb +8 -1
- data/lib/pgbus/process/consumer.rb +38 -1
- data/lib/pgbus/process/worker.rb +41 -0
- data/lib/pgbus/testing.rb +2 -1
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/job_context.rb +80 -0
- data/lib/pgbus.rb +2 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c419d130102e92e1bd355df6e98211e44eeead76298f9ce58bd6c285b1de24a9
|
|
4
|
+
data.tar.gz: dab0eefe9c7a23aee676b928521841adffb6d38f084993c6bb3ab56f9ade207f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8cedff08ffed91f3b9a9c1b9df1b2147790ec9bf7e5117b22a4af19b865b239df4883d6708153ec2efc9de5207e007cb4d2cfd06440ac3b4bd25e63d1eea369
|
|
7
|
+
data.tar.gz: fcb2ec48f7daf53dc46d5c388cb3b4e1acd99a568e74e92253736efb8ba4bc52fd9704d287fbaafcea893d063676b3f68ceac3d82b79fc3906ef4d7bbc43dcd3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **`current_attributes` capture skips an unpersisted record instead of raising at enqueue (issue #435).** An attribute holding an Active Record instance with no id — a dev-mode fallback record assigned to `Current`, a form-built model captured before `save`, a destroyed record whose locate is guaranteed to fail — made every `perform_later` in that context raise `Pgbus::CurrentAttributesError`, even though such a record can never round-trip (no id → no GlobalID) and capture is ambient: the enqueuer never opted into persisting that attribute per-call, so its momentary state must not abort the enqueue. `capture` now skips any attribute value that answers `persisted?` falsey (so destroyed-but-id-bearing records are skipped too, not just `new_record?`) with a debug log naming the class, attribute and why; the rest of the class's attributes still persist. The `except:` guidance and the loud `CurrentAttributesError` remain for genuinely unserializable values — objects without the Active Record duck-type (`respond_to?(:persisted?)`) are untouched. Applies to jobs and event-bus publish alike (same capture path). Refs #435.
|
|
6
|
+
|
|
3
7
|
### Added
|
|
4
8
|
|
|
9
|
+
- **First-class `Current` support: ActiveSupport::CurrentAttributes persist across enqueue → perform (issue #430).** The executor already reset `CurrentAttributes` around every job (so nothing leaked) but nothing restored it — `Current.tenant` was always nil inside a job. New `config.current_attributes` (`nil` = off; `:auto` = every `ActiveSupport::CurrentAttributes` subclass; an Array of classes/names; or a Hash of class => `{ only: [...] }` / `{ except: [...] }`). The new `Pgbus::ActiveJob::CurrentAttributes` mixin (included on `ActiveJob::Base` by the engine next to `BatchId`) captures the assigned attributes of each persisted class in `serialize` — serialized with `ActiveJob::Arguments`, so records become GlobalIDs and fall under the `allowed_global_id_models` allowlist on the way back — under the job-hash key `pgbus_current`, and restores them by wrapping the **whole** `perform_now` in nested `Current.set`, so `before_perform`, `perform`, `rescue_from`, `retry_on` / `discard_on` blocks and jobs enqueued from inside `perform` all see the context, under the pgbus worker and Rails' `:test` / `:inline` adapters alike. A deserialized job re-serializes the context it was enqueued with, so a `retry_on` re-enqueue keeps the original; concurrency-blocked promotion, dead-letter / dashboard retry and `perform_all_later` carry it by construction. An unserializable attribute raises `Pgbus::CurrentAttributesError` at `perform_later` naming the class, attribute and the `except:` fix — nothing is dropped silently; a class that no longer exists or an attribute no longer defined is skipped with a log line (Sidekiq parity). Per job class: `self.pgbus_persist_current_attributes = false` or a spec override. Unconfigured installs have byte-identical payloads. Dashboard: failed-job and dead-letter detail pages gain a **Context** card (`Pgbus::Web::JobContext`, through `PayloadFilter`). Event bus follows in #431. Refs #430.
|
|
10
|
+
|
|
11
|
+
- **Current attributes propagate publish → handler on the event bus (issue #431).** Follow-up to #430: `Pgbus.publish` from a request left `Current` empty in every event handler. With the same `config.current_attributes` switch on, the publisher now snapshots each persisted class (same `only:`/`except:` filters, same `ActiveJob::Arguments` serialization) into the **event envelope** under the same `pgbus_current` key jobs use (a sibling of `event_id`, never inside the user payload — `event.payload` is unchanged), PGMQ's topic fan-out copies it to every subscriber queue, and the handler pipeline restores it around `handle` (inside the Rails-executor wrap; previous values come back after each event — no cross-event leak on a consumer thread). `Pgbus::Event#context` exposes the raw stored form. `Pgbus::Outbox.publish_event` captures at write time — inside the app transaction, where `Current` is set — and the relay carries the envelope unchanged. GlobalIDs inside the context are gated by `allowed_global_id_models` in `Handler#build_event` before anything is located, the same boundary jobs gate at. `Pgbus::Testing` round-trips it (fake-mode events expose `context`; `drain!` and inline dispatch restore it), and the dashboard's pending-events rows show the same Context card as the failed-job pages. Default-off is byte-identical. Refs #431.
|
|
12
|
+
- **Fix: outbox events now dispatch to handlers.** `Outbox.publish_event` built its envelope without `routing_key` and `pgmq.send_topic` stamps nothing on the message, so a relayed outbox event reached the right subscriber queues but the consumer read no routing key, matched **zero** handlers, and archived the event unrun. The envelope now carries `routing_key:` exactly like a direct `Pgbus.publish`. Entries written by older code (already relayed or pending) still lack it — re-publish those events if any are pending when you upgrade. Refs #431.
|
|
13
|
+
- **Fair share scheduling for event-bus consumers (issue #427).** Follow-up to #426: a subscriber queue is FIFO, so a bulk import emitting `orders.created` 100 000 times for one tenant put every other tenant's events behind it in every handler subscribed to that topic. New `config.event_fair_share = ->(event) { key | [key, weight] | nil }` — the event twin of `fair_share` — receives the `Pgbus::Event` (routing key, the payload object as passed to publish, headers) at publish time on every path (`Pgbus.publish`, `publish_later`, `Pgbus::Outbox.publish_event`) and merges the same `pgbus_fair_key` / `pgbus_fair_weight` into the **event envelope** (a sibling of `event_id` / `published_at`, never inside the user payload, so `event.payload` is unchanged in handlers). Because the key lives at `message->>'pgbus_fair_key'` for events exactly as for jobs, #426's `read_batch_fair` SQL and `q_<queue>_fair_idx` index are reused verbatim; PGMQ's topic fan-out copies the tag to every bound subscriber queue, and the outbox (which stores the envelope) carries it across the relay with no extra work — a system writing `pgbus_outbox_entries` directly can set the key in the envelope JSON. `Consumer#fetch_messages` now fair-reads each active subscriber queue in list order with the remaining capacity (strict across queues, fair within; circuit-breaker-paused queues still skipped), ensures the fair index on its queues at boot, and `Subscriber#setup!` ensures it at queue creation. Independent of `fair_share` (enable either side or both); no interaction with `group_mode` (worker-only). Default-off is byte-identical. Refs #427.
|
|
14
|
+
- **Fair share scheduling across tenants — weighted, work-conserving (issue #426).** A queue is FIFO, so one tenant enqueuing 100 000 jobs put every other tenant's work behind them; nothing in pgbus prevented it (multi-queue reads are strict list-order, `group_mode` serializes PGMQ FIFO groups and pgbus never set its header for jobs, `limits_concurrency` is a non-work-conserving cap). New `config.fair_share = ->(job) { key | [key, weight] | nil }` is evaluated at enqueue on every adapter path (`enqueue`, `enqueue_at`, `perform_all_later`); the key and weight ride inside the job payload as `pgbus_fair_key` / `pgbus_fair_weight` (same pattern as `pgbus_concurrency_key`, so they survive blocked-execution promotion, DLQ retry and dashboard retry). Workers then read with the new `Client#read_batch_fair` — one statement that enumerates the keys with visible messages via a loose index scan, ranks each key's oldest visible messages, and takes the `qty` lowest `rank / weight`: weight 3 vs 1 is a 3:1 split while both have work, a lone tenant still fills the whole batch, and the cost scales with the number of keys that have visible work, not backlog depth (`rake bench:fair_read`: 1.05 ms with 1 key, 4.7 ms with 200 keys, 100k-row backlog; numbers in `docs/performance.md`). Strict list-order priority across queues is preserved (fair within each queue); with `priority_levels` it is strict between levels and fair within a level; mutually exclusive with `group_mode` (`validate!` and `Worker.new` both raise). The supporting expression index `q_<queue>_fair_idx ((COALESCE(message->>'pgbus_fair_key','')), vt, msg_id)` is created at queue creation when the option is on, and built `CONCURRENTLY` by each worker for the queues it already serves (`Client#ensure_fair_index`, memoized, logs the `DROP INDEX` remediation if a concurrent build is interrupted). Within a key messages are taken oldest-visible first (`vt, msg_id`) — a deliberate deviation from `pgmq.read`'s pure `msg_id` order so a tenant's whole visible backlog is never sorted per read. Event-bus consumers get the same in #427 (below). Refs #426.
|
|
15
|
+
|
|
5
16
|
- **Batches v2: open batches, a `batch` accessor on jobs and callbacks, and configured callback instances (issue #415).** ⚠️ **Breaking (pre-1.0)**: `Pgbus::Batch.find` now returns a rehydrated `Pgbus::Batch` handle instead of the raw attributes Hash — read the values off the handle (`status`, `total_jobs`, `completed_jobs`, `failed_jobs`, `pending_jobs`, `progress_percentage`, `finished?`, `description`, `properties`), or query `Pgbus::BatchEntry` for a row. Three capabilities: **(1) Open batches.** `batch.enqueue` is re-callable while the batch is unfinished — the second and later calls add to the existing group (`BatchEntry.increment_total_jobs!`, guarded on an unfinished row) instead of hitting the unique index on `batch_id`, so multi-stage workflows are possible. Adding to a finished batch raises `Pgbus::Batch::AlreadyFinished`. Execution rows are inserted as the block enqueues, before `total_jobs` is bumped, so the single-winner finish always sees outstanding work; a `check_finished!` after the bump covers a block whose jobs all completed while it was still open. Membership stays explicit: only jobs enqueued inside an `enqueue` block join the batch, so a fan-out from a batched job never silently extends it. **(2) `batch` accessor.** New `Pgbus::ActiveJob::BatchId` mixin (included on `ActiveJob::Base` from the engine alongside `Concurrency` and `Uniqueness`) adds `batch_id` / `callback_batch_id` accessors, round-trips both through `serialize`/`deserialize` — omitted from the payload when unset, so an unbatched job's serialized hash is unchanged — and exposes a memoized `batch` reader. The executor assigns `batch_id` from the payload's `pgbus_batch_id` before `perform`, so a running job can call `batch.enqueue` to add siblings. **(3) Configured callback instances.** `on_finish:` / `on_success:` / `on_failure:` accept an ActiveJob instance as well as a class: `on_finish: ReportJob.new.set(queue: :critical, wait: 5.minutes)`. `.set` options resolve at batch-creation time (matching solid_queue) into new `on_finish_job` / `on_success_job` / `on_failure_job` jsonb columns; at fire time the job is deserialized, given `callback_batch_id`, has its own `batch_id` cleared (a callback is never a member of the batch it reports on) and is enqueued on its configured queue and `scheduled_at`. Legacy class-name callbacks keep the `perform_later(properties)` signature — deprecated at 1.0 in favour of `batch.properties`. The dashboard shows a configured callback's `job_class`. Existing installs: `rails generate pgbus:add_batch_callback_jobs` (or `pgbus:update`); apps that have not migrated keep storing bare classes only. Refs #415.
|
|
6
17
|
|
|
7
18
|
- **Batch completion is now self-healing via execution-row tracking (issue #414).** ⚠️ **Restart pgbus workers after `rails generate pgbus:add_batch_executions` / `db:migrate`** — a process that still writes `discarded_jobs` will error once that column is gone. `pgbus_batches` no longer finishes on `completed + discarded == total` — that arithmetic stalled a batch *forever* if a worker crashed between `archive_from` and `signal_batch_completed`, and a crash mid-`Batch#enqueue` left an immortal `pending` row (`cleanup_batches` only deletes `finished`). Each batched job now gets a `pgbus_batch_executions` row (identity = ActiveJob `job_id`) inserted *before* `send_message`, with `msg_id` + `queue_name` backfilled after send. The batch finishes when no rows remain (single-winner `UPDATE … AND NOT EXISTS`, plus a fresh `exists?` re-check for Postgres READ COMMITTED). A dispatcher sweep (`config.batch_sweep_interval`, default 5 minutes) repairs four crash windows: stale rows whose PGMQ message is gone (archived → completed, DLQ → failed, missing → completed with a warning); orphan rows with `msg_id` NULL older than 5 minutes (enqueue crashed between insert and send — blocked concurrency jobs are excluded); stalled `pending` batches; stalled `processing` batches with zero rows. `on_failure:` / `failed_jobs` are canonical (`on_discard:` / `discarded_jobs` remain deprecated aliases until 1.0). `config.batch_retention` (default 7 days, `nil` disables) replaces the hardcoded cleanup window. Existing installs: `rails generate pgbus:add_batch_executions` (or `pgbus:update`). Unmigrated apps keep the counter path via `Batch.executions_migrated?`. Refs #414.
|
data/README.md
CHANGED
|
@@ -646,6 +646,36 @@ end
|
|
|
646
646
|
|
|
647
647
|
When `priority_levels` is `nil` (default), priority queues are disabled and all jobs go to a single queue per logical name.
|
|
648
648
|
|
|
649
|
+
### Fair share across tenants
|
|
650
|
+
|
|
651
|
+
One tenant enqueuing 100 000 jobs must not put every other tenant's work behind them. `config.fair_share` tags each job with a key (and optional weight) at enqueue, and the worker's read interleaves across keys inside each queue — a weighted, work-conserving round-robin. No per-tenant queues.
|
|
652
|
+
|
|
653
|
+
```ruby
|
|
654
|
+
Pgbus.configure do |config|
|
|
655
|
+
# key (String/Symbol/Integer), [key, weight], or nil to leave a job unkeyed
|
|
656
|
+
config.fair_share = ->(job) { [Current.tenant&.id, Current.tenant&.plan_weight || 1] }
|
|
657
|
+
end
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
Weight 3 vs 1 yields a 3:1 split while both have work; a lone tenant still gets the whole worker. Composes with `priority_levels` (strict between levels, fair within) and `limits_concurrency`; mutually exclusive with `group_mode`. Workers build the supporting index `CONCURRENTLY` on queues they already serve. Details: [Routing & ordering](https://pgbus.zoolutions.llc/docs/routing-ordering).
|
|
661
|
+
|
|
662
|
+
The event bus has the same knob: `config.event_fair_share = ->(event) { key | [key, weight] | nil }` receives the `Pgbus::Event` at publish (`Pgbus.publish`, `publish_later`, `Outbox.publish_event`), tags the event envelope — never your payload — and consumers interleave reads across keys inside each subscriber queue. Details: [Event bus](https://pgbus.zoolutions.llc/docs/event-bus).
|
|
663
|
+
|
|
664
|
+
### Current attributes
|
|
665
|
+
|
|
666
|
+
`ActiveSupport::CurrentAttributes` is reset around every job. Ask pgbus to carry it and `Current.tenant` / `Current.user` / `Current.request_id` are there inside `perform` — and inside `retry_on` / `discard_on` blocks, under the pgbus worker and Rails' `:test` / `:inline` adapters alike:
|
|
667
|
+
|
|
668
|
+
```ruby
|
|
669
|
+
Pgbus.configure do |config|
|
|
670
|
+
config.current_attributes = :auto # or [Current, "Admin::Current"], or { Current => { except: [:request] } }
|
|
671
|
+
config.fair_share = ->(job) { Current.tenant&.id } # pairs naturally with fair share
|
|
672
|
+
end
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
Events get the same hop: `Pgbus.publish` captures `Current` into the event envelope and the consumer restores it around every `handle` — including across the transactional outbox (captured at `Outbox.publish_event`, inside your transaction). Handlers can also read the raw form via `event.context`.
|
|
676
|
+
|
|
677
|
+
Captured at enqueue via `ActiveJob::Arguments` (records become GlobalIDs, gated by `allowed_global_id_models`), preserved across retries, concurrency-blocked promotion, dead-letter retry and `perform_all_later`; an unserializable attribute raises at `perform_later` with the `except:` fix, while an **unpersisted** record (no id — it could never be restored) is skipped with a debug log instead of aborting the enqueue. The dashboard shows the context on failed-job and dead-letter pages. Details: [Active Job → Current attributes](https://pgbus.zoolutions.llc/docs/active-job).
|
|
678
|
+
|
|
649
679
|
### Consumer priority
|
|
650
680
|
|
|
651
681
|
When multiple workers subscribe to the same queues, higher-priority workers process messages first. Lower-priority workers back off (3x polling interval) when a higher-priority worker is active.
|
|
@@ -2176,6 +2206,9 @@ Curated headline options for the README. The full operator reference (with types
|
|
|
2176
2206
|
| `priority_levels` | `nil` | Number of priority sub-queues (nil = disabled, 2-10) |
|
|
2177
2207
|
| `default_priority` | `1` | Default priority for jobs without explicit priority |
|
|
2178
2208
|
| `group_mode` | `nil` | Grouped-read ordering mode for a queue. Experimental — exempt from the 1.0 stability promise. |
|
|
2209
|
+
| `fair_share` | `nil` | Callable `->(job) { key \| [key, weight] \| nil }` evaluated at enqueue; workers interleave reads across keys (weighted, work-conserving). Mutually exclusive with `group_mode` |
|
|
2210
|
+
| `event_fair_share` | `nil` | Event-bus twin of `fair_share`: callable `->(event) { key \| [key, weight] \| nil }` receiving the `Pgbus::Event` at publish; consumers interleave reads across keys within each subscriber queue. Independent of `fair_share` |
|
|
2211
|
+
| `current_attributes` | `nil` | Persist `ActiveSupport::CurrentAttributes` across enqueue → perform: `:auto`, an Array of classes/names, or `{ Current => { except: [...] } }`. Restored around the whole `perform_now` |
|
|
2179
2212
|
| `archive_retention` | `7.days` | How long to keep archived messages. Accepts seconds, Duration, or `nil` to disable cleanup |
|
|
2180
2213
|
| `outbox_enabled` | `false` | Enable transactional outbox poller process |
|
|
2181
2214
|
| `outbox_poll_interval` | `1.0` | Seconds between outbox poll cycles |
|
data/Rakefile
CHANGED
|
@@ -25,7 +25,7 @@ namespace :bench do
|
|
|
25
25
|
# no-DB unit suite that bench:all runs in CI.
|
|
26
26
|
db_benches = %w[connection_pool_bench integration_bench streams_bench streams_read_pool_bench
|
|
27
27
|
execution_modes_bench pool_swap_bench pool_autoscale_bench job_burst_bench
|
|
28
|
-
notify_wake_bench notify_chaos_bench streams_hub_bench].freeze
|
|
28
|
+
notify_wake_bench notify_chaos_bench streams_hub_bench fair_read_bench].freeze
|
|
29
29
|
# The unit suite is every *_bench.rb that doesn't need a database, derived
|
|
30
30
|
# from the directory so a new unit bench is picked up automatically (kept in
|
|
31
31
|
# sync with bench:one, which globs the same files).
|
|
@@ -60,6 +60,11 @@ namespace :bench do
|
|
|
60
60
|
ruby "benchmarks/integration_bench.rb"
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
desc "Run fair share read benchmark (requires PGBUS_DATABASE_URL)"
|
|
64
|
+
task :fair_read do
|
|
65
|
+
ruby "benchmarks/fair_read_bench.rb"
|
|
66
|
+
end
|
|
67
|
+
|
|
63
68
|
desc "Run streams benchmarks (requires PGBUS_DATABASE_URL; boots real Puma + SSE)"
|
|
64
69
|
task :streams do
|
|
65
70
|
ruby "benchmarks/streams_bench.rb"
|
|
@@ -143,6 +143,13 @@ module Pgbus
|
|
|
143
143
|
tag.span(I18n.t("pgbus.helpers.batch_status.#{status}", default: status), class: css)
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
+
# Persisted Current attributes for a job payload, as
|
|
147
|
+
# { "Current" => { "tenant" => "gid://…", ... } } or nil (issue #430).
|
|
148
|
+
# Goes through pgbus_parse_message so PayloadFilter redaction applies.
|
|
149
|
+
def pgbus_job_context(message)
|
|
150
|
+
Pgbus::Web::JobContext.from_payload(pgbus_parse_message(message))
|
|
151
|
+
end
|
|
152
|
+
|
|
146
153
|
def pgbus_parse_message(message)
|
|
147
154
|
return {} unless message
|
|
148
155
|
|
|
@@ -26,6 +26,23 @@
|
|
|
26
26
|
</dl>
|
|
27
27
|
</div>
|
|
28
28
|
|
|
29
|
+
<% if (context = pgbus_job_context(@message[:message])) %>
|
|
30
|
+
<div class="rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700 p-6 mb-6" data-testid="job-context">
|
|
31
|
+
<h2 class="text-sm font-medium text-gray-500 mb-2"><%= t("pgbus.dead_letter.show.context") %></h2>
|
|
32
|
+
<% context.each do |klass_name, attrs| %>
|
|
33
|
+
<h3 class="text-xs font-semibold text-gray-700 dark:text-gray-300 mt-3 mb-1 font-mono"><%= klass_name %></h3>
|
|
34
|
+
<dl class="grid grid-cols-1 gap-x-4 gap-y-1 sm:grid-cols-2">
|
|
35
|
+
<% attrs.each do |name, value| %>
|
|
36
|
+
<div class="flex gap-2 text-xs">
|
|
37
|
+
<dt class="text-gray-500 font-mono"><%= name %></dt>
|
|
38
|
+
<dd class="text-gray-900 dark:text-white font-mono break-all"><%= value %></dd>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
41
|
+
</dl>
|
|
42
|
+
<% end %>
|
|
43
|
+
</div>
|
|
44
|
+
<% end %>
|
|
45
|
+
|
|
29
46
|
<div class="rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700 p-6 mb-6">
|
|
30
47
|
<h2 class="text-sm font-medium text-gray-500 mb-2"><%= t("pgbus.dead_letter.show.payload") %></h2>
|
|
31
48
|
<pre class="text-xs text-gray-600 bg-gray-50 dark:bg-gray-900 rounded p-4 overflow-x-auto"><%= JSON.pretty_generate(JSON.parse(@message[:message])) rescue @message[:message] %></pre>
|
|
@@ -102,6 +102,27 @@
|
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
104
|
</div>
|
|
105
|
+
<%# Persisted Current attributes (issue #431) %>
|
|
106
|
+
<% if (event_context = pgbus_job_context(m[:message])) %>
|
|
107
|
+
<div class="mb-3" data-testid="job-context">
|
|
108
|
+
<span class="text-xs font-medium text-gray-500"><%= t("pgbus.events.pending_table.context") %></span>
|
|
109
|
+
<div class="text-xs text-gray-600 bg-white dark:bg-gray-800 rounded p-2 mt-1 space-y-2">
|
|
110
|
+
<% event_context.each do |klass_name, attrs| %>
|
|
111
|
+
<div>
|
|
112
|
+
<p class="font-semibold text-gray-700 dark:text-gray-300 font-mono"><%= klass_name %></p>
|
|
113
|
+
<dl class="grid grid-cols-1 gap-x-4 gap-y-0.5 sm:grid-cols-2">
|
|
114
|
+
<% attrs.each do |name, value| %>
|
|
115
|
+
<div class="flex gap-2">
|
|
116
|
+
<dt class="text-gray-500 font-mono"><%= name %></dt>
|
|
117
|
+
<dd class="text-gray-900 dark:text-white font-mono break-all"><%= value %></dd>
|
|
118
|
+
</div>
|
|
119
|
+
<% end %>
|
|
120
|
+
</dl>
|
|
121
|
+
</div>
|
|
122
|
+
<% end %>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<% end %>
|
|
105
126
|
<%# Edit Payload form %>
|
|
106
127
|
<details class="mt-2">
|
|
107
128
|
<summary class="text-xs font-medium text-indigo-600 cursor-pointer hover:text-indigo-500"><%= t("pgbus.events.pending_table.edit_payload") %></summary>
|
|
@@ -38,6 +38,23 @@
|
|
|
38
38
|
</div>
|
|
39
39
|
<% end %>
|
|
40
40
|
|
|
41
|
+
<% if (context = pgbus_job_context(@job["payload"])) %>
|
|
42
|
+
<div class="rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700 p-6 mb-6" data-testid="job-context">
|
|
43
|
+
<h2 class="text-sm font-medium text-gray-500 mb-2"><%= t("pgbus.jobs.show.context") %></h2>
|
|
44
|
+
<% context.each do |klass_name, attrs| %>
|
|
45
|
+
<h3 class="text-xs font-semibold text-gray-700 dark:text-gray-300 mt-3 mb-1 font-mono"><%= klass_name %></h3>
|
|
46
|
+
<dl class="grid grid-cols-1 gap-x-4 gap-y-1 sm:grid-cols-2">
|
|
47
|
+
<% attrs.each do |name, value| %>
|
|
48
|
+
<div class="flex gap-2 text-xs">
|
|
49
|
+
<dt class="text-gray-500 font-mono"><%= name %></dt>
|
|
50
|
+
<dd class="text-gray-900 dark:text-white font-mono break-all"><%= value %></dd>
|
|
51
|
+
</div>
|
|
52
|
+
<% end %>
|
|
53
|
+
</dl>
|
|
54
|
+
<% end %>
|
|
55
|
+
</div>
|
|
56
|
+
<% end %>
|
|
57
|
+
|
|
41
58
|
<div class="rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700 p-6 mb-6">
|
|
42
59
|
<h2 class="text-sm font-medium text-gray-500 mb-2"><%= t("pgbus.jobs.show.payload") %></h2>
|
|
43
60
|
<pre class="text-xs text-gray-600 bg-gray-50 dark:bg-gray-900 rounded p-4 overflow-x-auto"><%= JSON.pretty_generate(pgbus_parse_message(@job["payload"])) %></pre>
|
data/config/locales/da.yml
CHANGED
|
@@ -131,6 +131,7 @@ da:
|
|
|
131
131
|
total_label: beskeder i dead letter-kø
|
|
132
132
|
show:
|
|
133
133
|
back: Tilbage til DLQ
|
|
134
|
+
context: Kontekst
|
|
134
135
|
discard: Kassér
|
|
135
136
|
discard_confirm: Slet permanent?
|
|
136
137
|
headers: Overskrifter
|
|
@@ -194,6 +195,7 @@ da:
|
|
|
194
195
|
title: Begivenheder
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Payload
|
|
198
|
+
context: Kontekst
|
|
197
199
|
discard: Forkast
|
|
198
200
|
discard_confirm: Forkast denne begivenhed? Den vil blive arkiveret.
|
|
199
201
|
edit_payload: Rediger & prøv igen
|
|
@@ -372,6 +374,7 @@ da:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Tilbage til job
|
|
374
376
|
backtrace: Stacktrace
|
|
377
|
+
context: Kontekst
|
|
375
378
|
discard: Kassér
|
|
376
379
|
discard_confirm: Slet dette job permanent?
|
|
377
380
|
error_message: Fejlmeddelelse
|
data/config/locales/de.yml
CHANGED
|
@@ -131,6 +131,7 @@ de:
|
|
|
131
131
|
total_label: Nachrichten in der Dead-Letter-Warteschlange
|
|
132
132
|
show:
|
|
133
133
|
back: Zurück zur DLQ
|
|
134
|
+
context: Kontext
|
|
134
135
|
discard: Verwerfen
|
|
135
136
|
discard_confirm: Dauerhaft verwerfen?
|
|
136
137
|
headers: Header
|
|
@@ -194,6 +195,7 @@ de:
|
|
|
194
195
|
title: Ereignisse
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Nutzlast
|
|
198
|
+
context: Kontext
|
|
197
199
|
discard: Verwerfen
|
|
198
200
|
discard_confirm: Dieses Ereignis verwerfen? Es wird archiviert.
|
|
199
201
|
edit_payload: Bearbeiten & erneut versuchen
|
|
@@ -372,6 +374,7 @@ de:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Zurück zu Jobs
|
|
374
376
|
backtrace: Stacktrace
|
|
377
|
+
context: Kontext
|
|
375
378
|
discard: Verwerfen
|
|
376
379
|
discard_confirm: Diesen Auftrag dauerhaft verwerfen?
|
|
377
380
|
error_message: Fehlermeldung
|
data/config/locales/en.yml
CHANGED
|
@@ -131,6 +131,7 @@ en:
|
|
|
131
131
|
total_label: messages in dead letter queue
|
|
132
132
|
show:
|
|
133
133
|
back: Back to DLQ
|
|
134
|
+
context: Context
|
|
134
135
|
discard: Discard
|
|
135
136
|
discard_confirm: Permanently discard?
|
|
136
137
|
headers: Headers
|
|
@@ -194,6 +195,7 @@ en:
|
|
|
194
195
|
title: Events
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Payload
|
|
198
|
+
context: Context
|
|
197
199
|
discard: Discard
|
|
198
200
|
discard_confirm: Discard this event? It will be archived.
|
|
199
201
|
edit_payload: Edit & Retry
|
|
@@ -372,6 +374,7 @@ en:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Back to Jobs
|
|
374
376
|
backtrace: Backtrace
|
|
377
|
+
context: Context
|
|
375
378
|
discard: Discard
|
|
376
379
|
discard_confirm: Discard this job permanently?
|
|
377
380
|
error_message: Error Message
|
data/config/locales/es.yml
CHANGED
|
@@ -131,6 +131,7 @@ es:
|
|
|
131
131
|
total_label: mensajes en la cola de mensajes fallidos
|
|
132
132
|
show:
|
|
133
133
|
back: Volver a DLQ
|
|
134
|
+
context: Contexto
|
|
134
135
|
discard: Descartar
|
|
135
136
|
discard_confirm: "¿Descartar permanentemente?"
|
|
136
137
|
headers: Encabezados
|
|
@@ -194,6 +195,7 @@ es:
|
|
|
194
195
|
title: Eventos
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Carga útil
|
|
198
|
+
context: Contexto
|
|
197
199
|
discard: Descartar
|
|
198
200
|
discard_confirm: "¿Descartar este evento? Será archivado."
|
|
199
201
|
edit_payload: Editar y reintentar
|
|
@@ -372,6 +374,7 @@ es:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Volver a Trabajos
|
|
374
376
|
backtrace: Rastreo de pila
|
|
377
|
+
context: Contexto
|
|
375
378
|
discard: Descartar
|
|
376
379
|
discard_confirm: "¿Descartar este trabajo permanentemente?"
|
|
377
380
|
error_message: Mensaje de error
|
data/config/locales/fi.yml
CHANGED
|
@@ -131,6 +131,7 @@ fi:
|
|
|
131
131
|
total_label: viestiä dead letter -jonossa
|
|
132
132
|
show:
|
|
133
133
|
back: Takaisin DLQ:hon
|
|
134
|
+
context: Konteksti
|
|
134
135
|
discard: Hylkää
|
|
135
136
|
discard_confirm: Hylätään pysyvästi?
|
|
136
137
|
headers: Otsikot
|
|
@@ -194,6 +195,7 @@ fi:
|
|
|
194
195
|
title: Tapahtumat
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Kuorma
|
|
198
|
+
context: Konteksti
|
|
197
199
|
discard: Hylkää
|
|
198
200
|
discard_confirm: Hylätäänkö tämä tapahtuma? Se arkistoidaan.
|
|
199
201
|
edit_payload: Muokkaa ja yritä uudelleen
|
|
@@ -372,6 +374,7 @@ fi:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Takaisin töihin
|
|
374
376
|
backtrace: Takautumisketju
|
|
377
|
+
context: Konteksti
|
|
375
378
|
discard: Hylkää
|
|
376
379
|
discard_confirm: Hylkää tämä työ pysyvästi?
|
|
377
380
|
error_message: Virheilmoitus
|
data/config/locales/fr.yml
CHANGED
|
@@ -131,6 +131,7 @@ fr:
|
|
|
131
131
|
total_label: messages dans la file de lettres mortes
|
|
132
132
|
show:
|
|
133
133
|
back: Retour à la DLQ
|
|
134
|
+
context: Contexte
|
|
134
135
|
discard: Ignorer
|
|
135
136
|
discard_confirm: Ignorer définitivement ?
|
|
136
137
|
headers: En-têtes
|
|
@@ -194,6 +195,7 @@ fr:
|
|
|
194
195
|
title: Événements
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Charge utile
|
|
198
|
+
context: Contexte
|
|
197
199
|
discard: Jeter
|
|
198
200
|
discard_confirm: Jeter cet événement ? Il sera archivé.
|
|
199
201
|
edit_payload: Modifier & Réessayer
|
|
@@ -372,6 +374,7 @@ fr:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Retour aux travaux
|
|
374
376
|
backtrace: Trace de la pile
|
|
377
|
+
context: Contexte
|
|
375
378
|
discard: Ignorer
|
|
376
379
|
discard_confirm: Supprimer ce travail définitivement ?
|
|
377
380
|
error_message: Message d'erreur
|
data/config/locales/it.yml
CHANGED
|
@@ -131,6 +131,7 @@ it:
|
|
|
131
131
|
total_label: messaggi nella coda dei messaggi non recapitati
|
|
132
132
|
show:
|
|
133
133
|
back: Torna a DLQ
|
|
134
|
+
context: Contesto
|
|
134
135
|
discard: Scarta
|
|
135
136
|
discard_confirm: Scartare definitivamente?
|
|
136
137
|
headers: Intestazioni
|
|
@@ -194,6 +195,7 @@ it:
|
|
|
194
195
|
title: Eventi
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Payload
|
|
198
|
+
context: Contesto
|
|
197
199
|
discard: Scarta
|
|
198
200
|
discard_confirm: Scartare questo evento? Verrà archiviato.
|
|
199
201
|
edit_payload: Modifica e riprova
|
|
@@ -372,6 +374,7 @@ it:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Torna ai Lavori
|
|
374
376
|
backtrace: Traccia dello stack
|
|
377
|
+
context: Contesto
|
|
375
378
|
discard: Scarta
|
|
376
379
|
discard_confirm: Scartare definitivamente questo lavoro?
|
|
377
380
|
error_message: Messaggio di errore
|
data/config/locales/ja.yml
CHANGED
|
@@ -131,6 +131,7 @@ ja:
|
|
|
131
131
|
total_label: デッドレターキュー内のメッセージ
|
|
132
132
|
show:
|
|
133
133
|
back: DLQに戻る
|
|
134
|
+
context: コンテキスト
|
|
134
135
|
discard: 破棄
|
|
135
136
|
discard_confirm: 完全に破棄しますか?
|
|
136
137
|
headers: ヘッダー
|
|
@@ -194,6 +195,7 @@ ja:
|
|
|
194
195
|
title: イベント
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: ペイロード
|
|
198
|
+
context: コンテキスト
|
|
197
199
|
discard: 破棄
|
|
198
200
|
discard_confirm: このイベントを破棄しますか?アーカイブされます。
|
|
199
201
|
edit_payload: 編集して再試行
|
|
@@ -372,6 +374,7 @@ ja:
|
|
|
372
374
|
show:
|
|
373
375
|
back: ジョブに戻る
|
|
374
376
|
backtrace: バックトレース
|
|
377
|
+
context: コンテキスト
|
|
375
378
|
discard: 破棄
|
|
376
379
|
discard_confirm: このジョブを完全に破棄しますか?
|
|
377
380
|
error_message: エラーメッセージ
|
data/config/locales/nb.yml
CHANGED
|
@@ -131,6 +131,7 @@ nb:
|
|
|
131
131
|
total_label: meldinger i dead letter-kø
|
|
132
132
|
show:
|
|
133
133
|
back: Tilbake til DLQ
|
|
134
|
+
context: Kontekst
|
|
134
135
|
discard: Forkast
|
|
135
136
|
discard_confirm: Forkaste permanent?
|
|
136
137
|
headers: Overskrifter
|
|
@@ -194,6 +195,7 @@ nb:
|
|
|
194
195
|
title: Hendelser
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Nyttelast
|
|
198
|
+
context: Kontekst
|
|
197
199
|
discard: Forkast
|
|
198
200
|
discard_confirm: Forkast denne hendelsen? Den vil bli arkivert.
|
|
199
201
|
edit_payload: Rediger & prøv på nytt
|
|
@@ -372,6 +374,7 @@ nb:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Tilbake til jobber
|
|
374
376
|
backtrace: Tilbakekalling
|
|
377
|
+
context: Kontekst
|
|
375
378
|
discard: Forkast
|
|
376
379
|
discard_confirm: Slett denne jobben permanent?
|
|
377
380
|
error_message: Feilmelding
|
data/config/locales/nl.yml
CHANGED
|
@@ -131,6 +131,7 @@ nl:
|
|
|
131
131
|
total_label: berichten in dead letter-wachtrij
|
|
132
132
|
show:
|
|
133
133
|
back: Terug naar DLQ
|
|
134
|
+
context: Context
|
|
134
135
|
discard: Verwerpen
|
|
135
136
|
discard_confirm: Permanent verwerpen?
|
|
136
137
|
headers: Headers
|
|
@@ -194,6 +195,7 @@ nl:
|
|
|
194
195
|
title: Gebeurtenissen
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Payload
|
|
198
|
+
context: Context
|
|
197
199
|
discard: Verwijderen
|
|
198
200
|
discard_confirm: Deze gebeurtenis verwijderen? Deze wordt gearchiveerd.
|
|
199
201
|
edit_payload: Bewerken & Opnieuw Proberen
|
|
@@ -372,6 +374,7 @@ nl:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Terug naar Taken
|
|
374
376
|
backtrace: Stacktrace
|
|
377
|
+
context: Context
|
|
375
378
|
discard: Verwijderen
|
|
376
379
|
discard_confirm: Deze taak permanent verwijderen?
|
|
377
380
|
error_message: Foutmelding
|
data/config/locales/pt.yml
CHANGED
|
@@ -131,6 +131,7 @@ pt:
|
|
|
131
131
|
total_label: mensagens na fila de mensagens mortas
|
|
132
132
|
show:
|
|
133
133
|
back: Voltar para DLQ
|
|
134
|
+
context: Contexto
|
|
134
135
|
discard: Descartar
|
|
135
136
|
discard_confirm: Descartar permanentemente?
|
|
136
137
|
headers: Cabeçalhos
|
|
@@ -194,6 +195,7 @@ pt:
|
|
|
194
195
|
title: Eventos
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Payload
|
|
198
|
+
context: Contexto
|
|
197
199
|
discard: Descartar
|
|
198
200
|
discard_confirm: Descartar este evento? Ele será arquivado.
|
|
199
201
|
edit_payload: Editar e Tentar Novamente
|
|
@@ -372,6 +374,7 @@ pt:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Voltar para Trabalhos
|
|
374
376
|
backtrace: Rastreamento de Pilha
|
|
377
|
+
context: Contexto
|
|
375
378
|
discard: Descartar
|
|
376
379
|
discard_confirm: Descartar este trabalho permanentemente?
|
|
377
380
|
error_message: Mensagem de Erro
|
data/config/locales/sv.yml
CHANGED
|
@@ -131,6 +131,7 @@ sv:
|
|
|
131
131
|
total_label: meddelanden i dead letter-kö
|
|
132
132
|
show:
|
|
133
133
|
back: Tillbaka till DLQ
|
|
134
|
+
context: Kontext
|
|
134
135
|
discard: Kassera
|
|
135
136
|
discard_confirm: Kassera permanent?
|
|
136
137
|
headers: Rubriker
|
|
@@ -194,6 +195,7 @@ sv:
|
|
|
194
195
|
title: Händelser
|
|
195
196
|
pending_table:
|
|
196
197
|
arguments: Payload
|
|
198
|
+
context: Kontext
|
|
197
199
|
discard: Kassera
|
|
198
200
|
discard_confirm: Kassera denna händelse? Den kommer att arkiveras.
|
|
199
201
|
edit_payload: Redigera & Försök igen
|
|
@@ -372,6 +374,7 @@ sv:
|
|
|
372
374
|
show:
|
|
373
375
|
back: Tillbaka till jobb
|
|
374
376
|
backtrace: Backtrace
|
|
377
|
+
context: Kontext
|
|
375
378
|
discard: Kassera
|
|
376
379
|
discard_confirm: Kassera detta jobb permanent?
|
|
377
380
|
error_message: Felmeddelande
|
|
@@ -10,6 +10,7 @@ module Pgbus
|
|
|
10
10
|
payload_hash = Serializer.serialize_job_hash(active_job)
|
|
11
11
|
payload_hash = Concurrency.inject_metadata(active_job, payload_hash)
|
|
12
12
|
payload_hash = Uniqueness.inject_metadata(active_job, payload_hash)
|
|
13
|
+
payload_hash = FairShare.inject_metadata(active_job, payload_hash)
|
|
13
14
|
payload_hash = inject_batch_metadata(payload_hash, active_job: active_job)
|
|
14
15
|
|
|
15
16
|
if uniqueness_rejected?(active_job, payload_hash, queue: queue)
|
|
@@ -25,6 +26,7 @@ module Pgbus
|
|
|
25
26
|
payload_hash = Serializer.serialize_job_hash(active_job)
|
|
26
27
|
payload_hash = Concurrency.inject_metadata(active_job, payload_hash)
|
|
27
28
|
payload_hash = Uniqueness.inject_metadata(active_job, payload_hash)
|
|
29
|
+
payload_hash = FairShare.inject_metadata(active_job, payload_hash)
|
|
28
30
|
payload_hash = inject_batch_metadata(payload_hash, active_job: active_job)
|
|
29
31
|
delay = [(timestamp - Time.current.to_f).ceil, 0].max
|
|
30
32
|
|
|
@@ -262,7 +264,9 @@ module Pgbus
|
|
|
262
264
|
def enqueue_immediate(queue, jobs, priority: nil)
|
|
263
265
|
return if jobs.empty?
|
|
264
266
|
|
|
265
|
-
payloads = jobs.map
|
|
267
|
+
payloads = jobs.map do |j|
|
|
268
|
+
inject_batch_metadata(FairShare.inject_metadata(j, Serializer.serialize_job_hash(j)), track: false)
|
|
269
|
+
end
|
|
266
270
|
# One guarded increment for the whole bulk, not one per job.
|
|
267
271
|
Batch.track_enqueue(payloads) if Thread.current[:pgbus_batch_id]
|
|
268
272
|
physical = physical_queue(queue, priority)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgbus
|
|
4
|
+
module ActiveJob
|
|
5
|
+
# Carries ActiveSupport::CurrentAttributes across enqueue → perform
|
|
6
|
+
# (issue #430). Included on ActiveJob::Base by the engine next to BatchId.
|
|
7
|
+
#
|
|
8
|
+
# * +serialize+ snapshots the persisted Current classes
|
|
9
|
+
# (Pgbus::CurrentAttributes.capture) under +pgbus_current+. A job that
|
|
10
|
+
# was itself deserialized re-serializes the context it was enqueued
|
|
11
|
+
# with — so a +retry_on+ re-enqueue keeps the original context even if
|
|
12
|
+
# Current changed during the attempt. Nothing is added when the feature
|
|
13
|
+
# is off or no attribute is assigned: the payload is byte-for-byte what
|
|
14
|
+
# it was before this mixin existed.
|
|
15
|
+
# * +perform_now+ is wrapped (not an +around_perform+) so the restored
|
|
16
|
+
# context also covers +rescue_from+ / +retry_on+ / +discard_on+ blocks,
|
|
17
|
+
# which run in +perform_now+'s rescue outside the perform callbacks —
|
|
18
|
+
# and it works identically under the pgbus worker, Rails' :test and
|
|
19
|
+
# :inline adapters, and a bare +job.perform_now+.
|
|
20
|
+
#
|
|
21
|
+
# Per-class control: +self.pgbus_persist_current_attributes = false+
|
|
22
|
+
# (never persist for this job class) or a spec in the same shapes as
|
|
23
|
+
# +config.current_attributes+ (an Array / Hash) to replace the config's
|
|
24
|
+
# list for this class. +nil+ (default) follows the config.
|
|
25
|
+
module CurrentAttributes
|
|
26
|
+
extend ActiveSupport::Concern
|
|
27
|
+
|
|
28
|
+
included do
|
|
29
|
+
attr_accessor :pgbus_current_attributes
|
|
30
|
+
|
|
31
|
+
class_attribute :pgbus_persist_current_attributes, instance_writer: false, default: nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def serialize
|
|
35
|
+
data = super
|
|
36
|
+
captured = pgbus_current_attributes ||
|
|
37
|
+
Pgbus::CurrentAttributes.capture(override: self.class.pgbus_persist_current_attributes)
|
|
38
|
+
data[Pgbus::CurrentAttributes::METADATA_KEY] = captured if captured
|
|
39
|
+
data
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def deserialize(job_data)
|
|
43
|
+
super
|
|
44
|
+
self.pgbus_current_attributes = job_data[Pgbus::CurrentAttributes::METADATA_KEY]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def perform_now
|
|
48
|
+
Pgbus::CurrentAttributes.restore(pgbus_current_attributes) { super }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|