Repository navigation
Stage policy arguments and publish them in one step - #2397
Conversation
Re-applying a policy first reset a queue's or exchange's settings to the argument values and then applied the policy on top. Anyone reading in between saw the resource without its policy: publishes slipped past a policy's max-length with overflow=reject-publish, and messages to an exchange whose alternate exchange came from a policy were dropped as unroutable. Readers could also observe torn Int64? limits and effective-argument arrays that were being mutated in place. Policy arguments are now staged by clear_policy_arguments and apply_policy_argument and published by a new commit_policy_arguments hook. Queues keep their argument and policy derived settings in one Settings object that is built off to the side and swapped in by reference, and side effects (expire fibers, limit enforcement, stream store limits) run after it is published. Exchanges stage their alternate exchange and effective arguments the same way, and PolicyTarget replaces effective_policy_args instead of clearing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mqv3W4rh4BxJDC4Y587DV9
|
No issues found. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mqv3W4rh4BxJDC4Y587DV9
commit_policy_arguments copied the staged DLX and DLRK into the DeadLetterer as two separate stores, and DeadLetterer#route read them separately, so a route on another thread could pair a new DLX with an old DLRK. The DeadLetterer now keeps both in one immutable Target that is swapped in by reference, and route reads it once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mqv3W4rh4BxJDC4Y587DV9
|
Fixed the DLX/DLRK pairing in f3b8c69.
Generated by Claude Code |
The publishers in the policy re-apply, alternate exchange and DLX/DLRK specs spun without yielding, so on the macOS runner, with fewer cores than the spec's threads, the fibers re-applying the policy were starved and the spec timed out. Publishers now yield every iteration and stop at the deadline themselves. They still fail on the code before the fix with two cores (taskset -c 0,1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mqv3W4rh4BxJDC4Y587DV9
|
Fixed in e9caa4d. The publishers in that spec, in the alternate-exchange spec and in the DLX/DLRK spec now yield every iteration and stop at the deadline themselves.
Generated by Claude Code |
The queue Settings, the dead letter target and the effective argument arrays were built and then published with a plain store. On a weakly ordered CPU (arm64) a reader on another thread could see the new reference before the writes that filled it in, e.g. a Settings without its max-length or overflow mode, which let a publish past the limit on the macOS (arm64) runner. They are now held in Atomics, set with :release and read with :acquire (an ldar on arm64, a plain load on x86). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mqv3W4rh4BxJDC4Y587DV9
|
Likely cause: a missing release barrier.
Fixed in 3013aff.
Generated by Claude Code |
|
I've re-run the failed jobs once. If either fails again I'll treat it as real. Generated by Claude Code |
โฆer-f2xdva-p3-policy-values
|
The
Generated by Claude Code |
โฆer-f2xdva-p3-policy-values # Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md # src/lavinmq/amqp/queue/queue.cr
|
The Docker Generated by Claude Code |
โฆer-f2xdva-p3-policy-values The delayed retry arguments from main move into Queue::Settings with the other argument and policy values: settings_from_arguments parses them, applies the default delivery limit and creates the retry queue, and the retry code reads them through settings.
|
Merged main, which includes #2254 (retries with backoff), in 53f2618. The Generated by Claude Code |
โฆer-f2xdva-p3-policy-values # Conflicts: # CHANGELOG.md
|
After merging main into this branch to resolve the CHANGELOG conflict, I reproduced the crash locally on main with Crystal 1.22.0. It happens in the Generated by Claude Code |
Crystal 1.22.0's Fiber::ExecutionContext::Isolated#wait_for sets `done = true` before it runs the event loop, so when the loop returns without a ready fiber, the isolated fiber is resumed anyway. That happens after a write: the socket is registered edge triggered for both read and write, and its write readiness event wakes nobody. A fiber waiting to read then retries with its event still queued on the fd, and the next wait_readable segfaults in Waiters#add. The Etcd::Lease keepalive does exactly that, so the etcd specs crashed the spec run on 1.22.0. Override wait_for on 1.22 with `done = false`, and require it where isolated contexts are started. (cherry picked from commit f2309a7)
Before Crystal 1.22, HTTP::Request#body = nil set Content-Length: 0 on POST and PUT requests, also on requests the server parsed, so parse_body returned an empty object for them. On 1.22 such a request has neither a body nor a content length, and parse_body answered "Request body required", e.g. to PUT /api/queues/vhost/name without a body. (cherry picked from commit 8bf5a08)
(cherry picked from commit 92aa32b)
Crystal's Options::ALL (0x80000BFF) includes bit 3, which is SSL_OP_ENABLE_KTLS in OpenSSL 3. Crystal 1.21 cleared ENABLE_KTLS again right after adding ALL in Context#initialize, 1.22 doesn't, so every TLS context had kTLS enabled, also when tls_ktls was off. On kernels with the tls module, the kTLS specs then found kTLS in effect. Clear ENABLE_KTLS after Context#initialize, as 1.21 did. The launcher still adds it when tls_ktls is on. (cherry picked from commit 36e16dd)
|
I ported #2440 ("Fix the spec suite on Crystal 1.22.0") into this branch as cherry-picks of its four commits, so CI is green again on the Crystal 1.22.0 image. #2440 has the Generated by Claude Code |
โฆer-f2xdva-p3-policy-values
|
No fix for this exists yet. I've re-run the job once. Proposed patch for case mode
when :fast
Fiber.yield if seq % 100 == 0
when :slowGenerated by Claude Code |
โฆer-f2xdva-p3-policy-values # Conflicts: # CHANGELOG.md
โฆer-f2xdva-p3-policy-values # Conflicts: # CHANGELOG.md # src/lavinmq/amqp/argument/dead_lettering.cr
|
Generated by Claude Code |
โฆer-f2xdva-p3-policy-values # Conflicts: # CHANGELOG.md
โฆer-f2xdva-p3-policy-values # Conflicts: # CHANGELOG.md
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
โน๏ธ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with ๐ while any review is running, comments if it has suggestions, and reacts with ๐ once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: โน๏ธ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with ๐. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
get, with_delivery_count_header and delete_message read the settings reference up to four times per delivered message. Load it once in get and pass it down, and check delivery counting with Settings#counts_deliveries?.
# Conflicts: # CHANGELOG.md
Why
Re-applying a policy first reset a queue's or exchange's settings to their argument values, then applied the policy on top. Anyone reading in between saw the resource with no policy at all:
max-lengthwithoverflow=reject-publish. In the new spec, about 600kโ880k publishes got through in 2s onmain.main.Int64?limits, and effective-argument arrays while they were being changed in place.This already happens on
main, whenever policies change while the queue is in use. It is one of the queue-internals steps towards running the default execution context on several threads.What
clear_policy_argumentsandapply_policy_argumentnow only stage the new values. A newPolicyTarget#commit_policy_argumentshook publishes them, andclear_policyuses the same path.Queue::Settingsobject: limits, TTLs, overflow mode, consumer timeout, DLX/DLRK, max-age and effective arguments.@msg_store_lock.PolicyTargetreplaceseffective_policy_argsinstead of clearing it in place.Specs:
spec/queue_policy_churn_spec.crandspec/alternate_exchange_spec.crfail onmainand pass with this change.spec/policies_spec.crnow reads values through@settings.๐ค Generated with Claude Code
https://claude.ai/code/session_01Mqv3W4rh4BxJDC4Y587DV9
Generated by Claude Code