Fix single-shot blocking-command effect and unsettled span on interrupt#154
Merged
Conversation
A blocking-command effect allocated its DedicatedPool.Lease when run(command) was called, so one lease was captured inside the returned effect value. CIO.ensure's finalizer cancels the lease on every exit (including success), driving it to the terminal Cancelled state, so re-executing the same value made Lease.attach fail its CAS, discard the connection, skip the submit, and never invoke the callback: the fiber hung silently. This was self-triggering via Paged.consume, which re-runs its single tailNew value each poll round, so xConsume stalled after one blocking round on the zio/ce/kyo backends. Acquire a fresh lease per execution in all three runtimes (standalone, cluster, master-replica) via acquireReleaseWith with the lease as the acquired resource. Also settle the tracked callback on interrupt so a started span still fires CommandCompleted: the cancel path released the connection but never invoked the callback. cancel now runs the settle action for a held connection, and leaseAndSubmit settles directly when attach finds the lease already cancelled (an interrupt in the offloaded acquire window or between redirect attempts, where cancel saw no held connection). The state CAS keeps this exclusive with the reply's finish, so the callback fires exactly once. Regression tests: re-running a blocking effect value twice (ZioSmokeSuite), and the interrupt/pre-attach settle paths (DedicatedPoolSpec).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two regressions from #152 in the blocking-command path, across all three runtimes.
H1 — a blocking-command effect is single-shot; re-running it hangs
val lease = new DedicatedPool.Leasewas evaluated whenrun(command)was called, so one lease got captured inside the returned effect value.CIO.ensure's finalizer runscancel()on every exit — including success — driving that lease to the terminalCancelledstate. Re-executing the same value (.retry,.repeat,.forever, …) madeLease.attachfail its CAS, discard the freshly acquired connection, skip the submit, and never invoke the callback: the fiber hangs silently.It was self-triggering in shipped code:
Paged.consumere-runs its singletailNeweffect value each poll round, soxConsumedelivered one blocking round and then stalled forever on the zio/ce/kyo backends.Fix: acquire a fresh lease per execution in the standalone, cluster, and master-replica runtimes, expressed as
acquireReleaseWithwith the lease as the acquired resource (CIO.deferfor the acquire —valueis strict on the CE backend and would allocate once).M1 — interrupting an in-flight blocking command never settles observability
The cancel path released the connection but never invoked the tracked callback, so the trace span settled zero times and no
CommandCompletedfired.Fix: thread an
onInterruptsettle action through the lease.cancelruns it for a held connection;leaseAndSubmitruns it directly whenattachfinds the lease already cancelled — an interrupt in the offloaded acquire window or between redirect attempts, wherecancelsaw no held connection and so could not settle the callback itself. ThestateCAS keeps this exclusive with the reply'sfinish, so the callback fires exactly once. It usesConnectionLost(mayHaveExecuted = true), which in the cluster reply path terminally completes (Fault.Lost(true)) rather than triggering a retry.Tests
ZioSmokeSuite: re-running the same blocking effect value twice succeeds each round (fails by hanging without the fix).DedicatedPoolSpec: interrupt of an in-flight command settles + discards the slot; interrupt before attach (offloaded-acquire window) still settles; acancelafter a normal reply does not re-fire.All six client backend cells compile;
DedicatedPoolSpec(20) andZioSmokeSuite(12) pass; scalafmt clean.