pgbus 0.15.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9e24beb40f37672bb2d5b51db5c6ffeb2894e1a2be3711ff6b17cc5c2ed52b7
4
- data.tar.gz: 4fe85e1733dd8257b1f13af40778fbc449146015d707e9d2889b287ad83af78b
3
+ metadata.gz: c419d130102e92e1bd355df6e98211e44eeead76298f9ce58bd6c285b1de24a9
4
+ data.tar.gz: dab0eefe9c7a23aee676b928521841adffb6d38f084993c6bb3ab56f9ade207f
5
5
  SHA512:
6
- metadata.gz: b8e23d2e38e12c2dd882dcdb5de8c23763b469551c869511718cd9d52a85ccdceb344779ae48ab3a3cfec2f1d3e8373d642f8d97f712c30f98893ed0450abc0d
7
- data.tar.gz: 142d408f3fa304d82593cf9fdf6167592c3bf2cafb64016702b057e374a98c7476e33922d6b05be00e7a71136861a77d9b8267dbd1fabb1dc6ed48c6fd1e15ae
6
+ metadata.gz: d8cedff08ffed91f3b9a9c1b9df1b2147790ec9bf7e5117b22a4af19b865b239df4883d6708153ec2efc9de5207e007cb4d2cfd06440ac3b4bd25e63d1eea369
7
+ data.tar.gz: fcb2ec48f7daf53dc46d5c388cb3b4e1acd99a568e74e92253736efb8ba4bc52fd9704d287fbaafcea893d063676b3f68ceac3d82b79fc3906ef4d7bbc43dcd3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
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
 
5
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.
data/README.md CHANGED
@@ -674,7 +674,7 @@ end
674
674
 
675
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
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. The dashboard shows the context on failed-job and dead-letter pages. Details: [Active Job → Current attributes](https://pgbus.zoolutions.llc/docs/active-job).
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
678
 
679
679
  ### Consumer priority
680
680
 
@@ -8,8 +8,11 @@ module Pgbus
8
8
  # (config.current_attributes: :auto, an explicit list, or per-class
9
9
  # only:/except: filters) serialized through ActiveJob::Arguments — so
10
10
  # GlobalID models, Symbols, Times round-trip like job arguments and fall
11
- # under the allowed_global_id_models allowlist on the way back. `restore`
12
- # nests `klass.set(attrs)` for the duration of a block.
11
+ # under the allowed_global_id_models allowlist on the way back. An
12
+ # attribute holding an unpersisted record is skipped with a debug log
13
+ # (no id → no GlobalID → it could never be restored; see
14
+ # #reject_unpersisted). `restore` nests `klass.set(attrs)` for the
15
+ # duration of a block.
13
16
  #
14
17
  # The ActiveJob side (Pgbus::ActiveJob::CurrentAttributes) calls capture
15
18
  # from `serialize` and restore around `perform_now`, so the hop works under
@@ -34,7 +37,7 @@ module Pgbus
34
37
  captured = {}
35
38
  persisted_specs(config, override: override).each do |spec|
36
39
  klass = resolve_class(spec[:name]) or next
37
- attrs = filter_attrs(klass.attributes, spec)
40
+ attrs = reject_unpersisted(klass, filter_attrs(klass.attributes, spec))
38
41
  next if attrs.empty?
39
42
 
40
43
  captured[klass.name] = serialize_attrs(klass, attrs)
@@ -126,6 +129,27 @@ module Pgbus
126
129
  attrs
127
130
  end
128
131
 
132
+ # An unpersisted record cannot round-trip by definition — no id, no
133
+ # GlobalID — and capture is ambient: the enqueuer never opted into
134
+ # persisting this attribute per-call, so its momentary state (a dev-mode
135
+ # fallback record, a form-built model assigned to Current before save,
136
+ # a destroyed record whose locate is guaranteed to fail) must not abort
137
+ # the enqueue. Skip it like an unassigned attribute. persisted? (not
138
+ # new_record?) so destroyed-but-id-bearing records are skipped too;
139
+ # objects without the Active Record duck-type pass through untouched
140
+ # and still hit serialize_attrs' loud CurrentAttributesError if bad.
141
+ def reject_unpersisted(klass, attrs)
142
+ attrs.reject do |name, value|
143
+ next false unless value.respond_to?(:persisted?) && !value.persisted?
144
+
145
+ Pgbus.logger.debug do
146
+ "[Pgbus] current_attributes: #{klass.name}##{name} holds an unpersisted #{value.class} — " \
147
+ "skipped (no id to serialize; it could never be restored)"
148
+ end
149
+ true
150
+ end
151
+ end
152
+
129
153
  def serialize_attrs(klass, attrs)
130
154
  ::ActiveJob::Arguments.serialize([attrs]).first
131
155
  rescue ::ActiveJob::SerializationError, URI::Error
data/lib/pgbus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgbus
4
- VERSION = "0.15.0"
4
+ VERSION = "0.15.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson