phlex-reactive 0.13.1 → 0.13.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 +18 -0
- data/README.md +11 -4
- data/lib/phlex/reactive/settles.rb +50 -13
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb580ca60e8e64b7857e665f34e5311e35f66c8aa4348135c61286456b06649f
|
|
4
|
+
data.tar.gz: fb25bb13bd78f0923f6a7a3c5ea383101cca16eaf9b808607e074c49dbbaa583
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d50f4794edea6b74bf4178df8d4a271e947e8bc3eef268d19ba8cb855c3207e87ee9c3bd345a22b3d9192f58510bb00860278c64a59c7f1e1b3478b72af62c0
|
|
7
|
+
data.tar.gz: '0233287800960c1886865f7df4fe2785237820d16e212b616e1971c879d0a97abd373f55588f2921aef4bd742d8cb053ffb2b560a81862517767dbd883556fac'
|
data/CHANGELOG.md
CHANGED
|
@@ -441,6 +441,24 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
441
441
|
|
|
442
442
|
### Fixed
|
|
443
443
|
|
|
444
|
+
- **`reply.pending` kept its settle handle under
|
|
445
|
+
`enqueue_after_transaction_commit = true` (#254).** The handle was captured in
|
|
446
|
+
`Phlex::Reactive::Settles#serialize`, which reads a thread-local that lives
|
|
447
|
+
only for the duration of `reply.pending`'s enqueue block. Under Rails'
|
|
448
|
+
`ActiveJob::Base.enqueue_after_transaction_commit = true` (the 7.2+
|
|
449
|
+
recommended setting) `job.enqueue` is deferred to
|
|
450
|
+
`ActiveRecord.after_all_transactions_commit`, and the endpoint runs every
|
|
451
|
+
action inside a transaction — so `serialize` always ran AFTER the block had
|
|
452
|
+
exited. The job serialized with no `phlex_reactive_settle` key,
|
|
453
|
+
`reactive_settle` was a no-op, and the row sat shimmering until someone
|
|
454
|
+
reloaded. The handle is now captured when the job INSTANCE is created
|
|
455
|
+
(`perform_later` → `job_or_instantiate` → `new`), which is synchronous inside
|
|
456
|
+
the block whether or not the enqueue itself is deferred; `serialize` prefers
|
|
457
|
+
the captured handle and still falls back to the thread-local. Both the
|
|
458
|
+
`job:`/`args:` sugar (narrowing per record, so failures stay attributable) and
|
|
459
|
+
the block form are covered, and a retry re-enqueue keeps its handle. Jobs
|
|
460
|
+
enqueued outside any `reply.pending` still carry nothing.
|
|
461
|
+
|
|
444
462
|
- **`rake release` bumps the pin in every tracked lockfile (#247, #253).** Since
|
|
445
463
|
#246 the gem root's `Gemfile.lock` is committed alongside `docs/Gemfile.lock`,
|
|
446
464
|
and both pin `phlex-reactive` by local path — so a version bump that left them
|
data/README.md
CHANGED
|
@@ -2591,10 +2591,17 @@ bug). It emits:
|
|
|
2591
2591
|
|
|
2592
2592
|
- **The handle rides ActiveJob metadata, not `perform`'s arity.** `reply.pending`
|
|
2593
2593
|
installs it in a thread-local and runs your enqueue inside it;
|
|
2594
|
-
`Settles#
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
almost always have
|
|
2594
|
+
`Settles#initialize` captures it onto the job instance and `#serialize` copies
|
|
2595
|
+
it into the job's metadata. So **every other caller of the same job — a
|
|
2596
|
+
nightly sweep, a webhook — keeps working unchanged**, and `reactive_settle` is
|
|
2597
|
+
simply a no-op there. That is load-bearing: these jobs almost always have
|
|
2598
|
+
non-UI callers.
|
|
2599
|
+
- **It works under `enqueue_after_transaction_commit = true`.** Rails defers that
|
|
2600
|
+
enqueue to `ActiveRecord.after_all_transactions_commit`, and the endpoint runs
|
|
2601
|
+
your action inside a transaction — so the enqueue (and its `serialize`) happens
|
|
2602
|
+
*after* the block has exited. The capture is therefore at job
|
|
2603
|
+
**instantiation**, which is synchronous inside the block either way. Nothing to
|
|
2604
|
+
configure.
|
|
2598
2605
|
- **A job that raises still clears the pending state — when it can attribute
|
|
2599
2606
|
it.** The `job:`/`args:` form enqueues one job per record and narrows each
|
|
2600
2607
|
job's handle to *that record's* target, so a failure clears exactly its row
|
|
@@ -27,15 +27,19 @@ module Phlex
|
|
|
27
27
|
# ## The handle rides ActiveJob metadata
|
|
28
28
|
#
|
|
29
29
|
# `reply.pending` installs a Pending::Handle in a thread-local and runs the
|
|
30
|
-
# caller's enqueue inside it; #
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
30
|
+
# caller's enqueue inside it; #initialize below captures it onto the job
|
|
31
|
+
# instance, #serialize copies it into the job's metadata, #deserialize
|
|
32
|
+
# restores it. So `perform`'s ARITY IS UNTOUCHED and every OTHER caller of
|
|
33
|
+
# the same job — a nightly sweep, a webhook — builds it with no handle, in
|
|
34
|
+
# which case `reactive_settle` is a NO-OP that returns nil. That is
|
|
35
|
+
# load-bearing: these jobs almost always have non-UI callers.
|
|
35
36
|
#
|
|
36
|
-
# (`perform_now` does
|
|
37
|
-
#
|
|
38
|
-
#
|
|
37
|
+
# (A `perform_now` INSIDE the enqueue block does carry the handle, since the
|
|
38
|
+
# instance is built there — so it settles synchronously. That is the right
|
|
39
|
+
# reading: `reply.pending` already marked the targets, and something has to
|
|
40
|
+
# resolve the shimmer. The settle's durable message is replayed from
|
|
41
|
+
# since-id 0 when the client opens the subscription, so arriving before it
|
|
42
|
+
# exists is safe.)
|
|
39
43
|
#
|
|
40
44
|
# ## A rolled-back action
|
|
41
45
|
#
|
|
@@ -57,12 +61,45 @@ module Phlex
|
|
|
57
61
|
# shared namespace with every other gem in the app.
|
|
58
62
|
SETTLE_METADATA_KEY = "phlex_reactive_settle"
|
|
59
63
|
|
|
60
|
-
# Capture the in-flight settle handle at
|
|
61
|
-
#
|
|
62
|
-
# is
|
|
63
|
-
#
|
|
64
|
+
# Capture the in-flight settle handle at INSTANTIATION (issue #254).
|
|
65
|
+
#
|
|
66
|
+
# `serialize` is the seam pgbus's own ActiveJob::CurrentAttributes
|
|
67
|
+
# integration uses, and it looked like the enqueue-time hook — but under
|
|
68
|
+
# Rails' `enqueue_after_transaction_commit = true` (the 7.2+ recommended
|
|
69
|
+
# setting) `job.enqueue` is deferred to
|
|
70
|
+
# ActiveRecord.after_all_transactions_commit, and the endpoint runs every
|
|
71
|
+
# action inside a transaction. So the deferral — and with it `serialize` —
|
|
72
|
+
# always fires AFTER reply.pending's `with_handle` block has exited, with
|
|
73
|
+
# an empty thread-local: no metadata key, a no-op `reactive_settle`, and a
|
|
74
|
+
# row left shimmering until someone reloads.
|
|
75
|
+
#
|
|
76
|
+
# `new` is the one moment guaranteed to be inside the block:
|
|
77
|
+
# `perform_later` → `job_or_instantiate` → `new` is synchronous, deferral
|
|
78
|
+
# or not. Pending#each_with_narrowed_handle narrows the thread-local per
|
|
79
|
+
# record BEFORE `perform_later`, so the `job:`/`args:` attribution
|
|
80
|
+
# contract is preserved.
|
|
81
|
+
def initialize(...)
|
|
82
|
+
super
|
|
83
|
+
@reactive_settle_handle ||= Phlex::Reactive::Pending.current_handle
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The same capture at ENQUEUE, for an instance built BEFORE the block and
|
|
87
|
+
# enqueued inside it (`job = MyJob.new(...)` … `reply.pending { job.enqueue }`).
|
|
88
|
+
# `enqueue` runs synchronously inside the block — it is the deferral it
|
|
89
|
+
# REGISTERS that runs later — so this is the last moment the thread-local
|
|
90
|
+
# is visible. `||=` never overwrites: a retry's `retry_job` re-enqueues a
|
|
91
|
+
# DESERIALIZED instance, which must keep the handle it came back with.
|
|
92
|
+
def enqueue(...)
|
|
93
|
+
@reactive_settle_handle ||= Phlex::Reactive::Pending.current_handle
|
|
94
|
+
super
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Prefer the captured handle; the thread-local fallback covers an instance
|
|
98
|
+
# serialized inside the block without going through either hook. A retry
|
|
99
|
+
# re-enqueue re-serializes the SAME instance, which keeps its handle — the
|
|
100
|
+
# right reading: the pending UI is still waiting on this work.
|
|
64
101
|
def serialize
|
|
65
|
-
handle = Phlex::Reactive::Pending.current_handle
|
|
102
|
+
handle = @reactive_settle_handle || Phlex::Reactive::Pending.current_handle
|
|
66
103
|
return super unless handle
|
|
67
104
|
|
|
68
105
|
super.merge(SETTLE_METADATA_KEY => handle.to_h_wire)
|