You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Journal reader per-object 1000ms budget trips on first read on hosted windows-2025 despite ~4ms I/O #851
Describe the bug Read-CapJournalObject intermittently fails with JournalUnavailable … deadline/exhaustion on hosted windows-2025 runners, even though the measured file I/O is only a few milliseconds:
#845 removed the success-path CopyCompleted flushed observation. The hypothesis was that it caused this, and that hypothesis is now refuted: the signature recurred on a head without it.
The per-object transaction budget is Min(1000 ms, lease remaining). Its clock starts in New-CapJournalTransaction, which runs before Read-CapJournalObject is invoked. Both occurrences are sequence=1, the first journal read in the process. The roughly 1.2 s that open and copy don't account for is spent inside the timed window, before the first Open.
Leading hypothesis, unproven: first-invocation cost of the large Read-CapJournalObject script block counts against the 1000 ms budget. That cost includes PowerShell compilation and AMSI/Defender script-block scanning on hosted runners.
Expected behavior
The per-object budget should bound journal I/O and retries, not one-time interpreter or AMSI costs. The absolute lease deadline must remain the hard bound.
A third occurrence on #850's single diagnostic rerun (head dea7de2a) confirms this is deterministic-enough on hosted windows-2025 to block capture-core PRs.
The suite was 82 executed, 79 passed, 3 failed. The third failure is the dependent report gate. Receipt-block passed this time, so its earlier first-attempt failure remains unattributed.
The sequence=3 failure refutes the "first invocation only" part of the hypothesis. The roughly 1.2 to 1.4 s that open and copy don't account for happens before the first Open, also on a later read.
Candidates:
work between New-CapJournalTransaction and the first Open in callers (for example Get-CapJournalNames directory enumeration);
script-block or AMSI cost per invocation.
Fix direction: start the per-object I/O budget at the first Open attempt, keep the absolute lease deadline as the hard bound, and report pre-open elapsed time in the failure message, failure path only.
Tracking relationship (operator-directed): #846, #849 and #851 form the capture-core flake set. This is an administrative grouping of distinct intermittent failures, not a claim that their root causes are identical or proven harmless flakes. #846 tracks actor exit/kernel/both-EOF evidence; #849 tracks unavailable Process Handle/Id during Snapshot attestation; #851 tracks journal per-object budget ownership/pre-open timing. Preserve each failed attempt and its exact signature independently; a passing diagnostic is not a source fix or causal explanation. Current execution order is #851 first, parked #850 mechanical rebase/requalification after that merge, then #849, then the separately leased live B attempt. No retry, gate weakening or live execution is granted by this comment.
#852's new failure-only artifact upload worked on its first real failure, and its preOpenMs data localizes the cost.
Windows-2025 job 117246916321 failed on real argument vector preserves an empty string and Unicode. The artifact file argument-vector/output/diagnostics/journal-read-observations.jsonl shows one Read-CapJournalSnapshot transaction:
Decision
totalElapsedMs
Notes
ReadVerified (seq 1)
98.4
preOpenMs 44.9, openMs 0.33, copyMs 2.45
AcceptedLiveSequenceOnly (seq 1)
895.3
DeadlineExhausted
1979.8
Open and copy are about 3 ms, and pre-open is about 45 ms. Moving the clock to the first Open attempt (#852's change) therefore doesn't address the cost. The roughly 1.9 s goes elsewhere, in the gaps after each success-path Write-CapJournalObservation call:
ReadVerified → AcceptedLiveSequenceOnly (about 800 ms) covers the ReadVerified observation write plus Get-CapJournalNames.
AcceptedLiveSequenceOnly → DeadlineExhausted (about 1084 ms) covers only the AcceptedLiveSequenceOnly observation write before the next Assert-CapJournalTime.
Write-CapJournalObservation opens in Append mode and calls Flush($true), which is FlushFileBuffers, a synchronous disk flush. On hosted runners that apparently costs up to about 1 s per call.
Correction to the earlier record: the "flushed write inside the budget" mechanism from #845 was right. #845 removed only one of the three success-path flushed observation writes, CopyCompleted. ReadVerified and AcceptedLiveSequenceOnly still flush inside the window.
Next fix: success-path observation writes must not perform a synchronous disk flush inside the transaction budget. An OS-buffered Flush() keeps the records durable across process termination, including kill-on-close. Leave the budget value unchanged.
Confirmation evidence: PR #852 was squash-merged as 77ec22136a99f55e7d0be00ce37979525753a57f at 2026-10-17T01:58:48Z, from exact reviewed R3 head bd6bb43a3850f418a5d9219799d1fbb50944cac8 and canonical body SHA-256 5173FC06EE6CABB374D6975140F92FBF8AB591EB48040B2556C0CE898539805F.
Both required hardening jobs passed on the FIRST attempt, without any diagnostic rerun:
Windows-2022 job 117272004103: actual UiCaptureDriver 72 executed / 72 passed / 0 failed, plus ValidationRunner.Tests.ps1: PASS.
Each retained whole job log contains exactly one JournalUnavailable string: the evidenced PASSing intentionally blocked observer-sink fixture diagnostic. No real/unclassified reader timeout or failing actor-cleanup case appeared in either run. Raw job-log SHA-256: Windows-2022 ADD17ABED3531ABAFE6B122914C6BF5D3BF91F4C5A0D5EB88967B0E8B1F12E35; Windows-2025 B0EFCD95F83ED05952B2533D163E490D25B94AA567A29F6152E4DC6CD3949FC9.
All nine required contexts and all nonrequired checks were accepted. Ready was requested at 01:55:46.4898393Z; a NEW Ready-event DCO run 38992894522, created at 01:55:50Z, passed before merge. The earlier synchronize DCO was not reused as Ready evidence. Final head/body/main/one-commit/five-file/review/CLEAN guards passed; a private compare-response metadata binding was corrected before Ready, without source changes, check bypass or CI rerun.
Persistence was independently checked: merge parent is 95a12681d6d3a6bbc3e1c64992489dd4d04f2863; whole tree d057d02a23ed68052a26be61b7530aa747e3a7f8 equals the reviewed tree; all eight qualified raw Git blobs and SHA-256 values match on the merged commit. The local R3 contained run separately passed 75/75 with retained lifecycle/journal proofs. Its disclosed concurrent D: deletion remains in the evidence; that local pass was accepted without a replacement or any claim of zero I/O impact.
This confirms the terminal-only durability policy on this hosted head, not a universal historical-cause or flakiness claim. Earlier failures remain preserved, including the R2 real reader timeout masked behind #846-shaped actor cleanup; see the recovered-primary record. Whole-observation timing is not isolated FlushFileBuffers timing. No GUI, visual parity, accessibility or macOS safety claim follows.
Leaving #851 OPEN for additional hosted Windows-2025 confirmation, including rebased #850. Closure will be proposed only after the further required runs are green. Next is the separate pre-existing production observer-mask issue, then mechanical #850 rebase, then #849; no live B admission is implied.
On both heads, every JournalUnavailable line in the logs was the expected line from the passing observer-sink fixture; there was no real timeout.
Root cause, as confirmed by the artifacts uploaded on failure: journal observation writes called Flush($true) (FlushFileBuffers) inside the 1000 ms per-object budget. That covered success-path writes and the retryable OpenFailed writes, and each forced write could take hundreds of milliseconds to about 1 s on hosted runners. #852 starts the budget at the first Open, and only terminal observations now flush durably. The budget value and retry semantics are unchanged.
Activity
coneilen commentedon Oct 16, 2026
A third occurrence on #850's single diagnostic rerun (head
dea7de2a) confirms this is deterministic-enough on hosted windows-2025 to block capture-core PRs.The suite was 82 executed, 79 passed, 3 failed. The third failure is the dependent report gate. Receipt-block passed this time, so its earlier first-attempt failure remains unattributed.
The
sequence=3failure refutes the "first invocation only" part of the hypothesis. The roughly 1.2 to 1.4 s that open and copy don't account for happens before the firstOpen, also on a later read.Candidates:
New-CapJournalTransactionand the firstOpenin callers (for exampleGet-CapJournalNamesdirectory enumeration);Fix direction: start the per-object I/O budget at the first
Openattempt, keep the absolute lease deadline as the hard bound, and report pre-open elapsed time in the failure message, failure path only.coneilen commentedon Oct 16, 2026
Tracking relationship (operator-directed): #846, #849 and #851 form the capture-core flake set. This is an administrative grouping of distinct intermittent failures, not a claim that their root causes are identical or proven harmless flakes. #846 tracks actor exit/kernel/both-EOF evidence; #849 tracks unavailable Process Handle/Id during Snapshot attestation; #851 tracks journal per-object budget ownership/pre-open timing. Preserve each failed attempt and its exact signature independently; a passing diagnostic is not a source fix or causal explanation. Current execution order is #851 first, parked #850 mechanical rebase/requalification after that merge, then #849, then the separately leased live B attempt. No retry, gate weakening or live execution is granted by this comment.
coneilen commentedon Oct 16, 2026
#852's new failure-only artifact upload worked on its first real failure, and its
preOpenMsdata localizes the cost.Windows-2025 job 117246916321 failed on
real argument vector preserves an empty string and Unicode. The artifact fileargument-vector/output/diagnostics/journal-read-observations.jsonlshows oneRead-CapJournalSnapshottransaction:ReadVerified(seq 1)AcceptedLiveSequenceOnly(seq 1)DeadlineExhaustedOpen and copy are about 3 ms, and pre-open is about 45 ms. Moving the clock to the first
Openattempt (#852's change) therefore doesn't address the cost. The roughly 1.9 s goes elsewhere, in the gaps after each success-pathWrite-CapJournalObservationcall:ReadVerified→AcceptedLiveSequenceOnly(about 800 ms) covers theReadVerifiedobservation write plusGet-CapJournalNames.AcceptedLiveSequenceOnly→DeadlineExhausted(about 1084 ms) covers only theAcceptedLiveSequenceOnlyobservation write before the nextAssert-CapJournalTime.Write-CapJournalObservationopens in Append mode and callsFlush($true), which isFlushFileBuffers, a synchronous disk flush. On hosted runners that apparently costs up to about 1 s per call.Correction to the earlier record: the "flushed write inside the budget" mechanism from #845 was right. #845 removed only one of the three success-path flushed observation writes,
CopyCompleted.ReadVerifiedandAcceptedLiveSequenceOnlystill flush inside the window.Next fix: success-path observation writes must not perform a synchronous disk flush inside the transaction budget. An OS-buffered
Flush()keeps the records durable across process termination, including kill-on-close. Leave the budget value unchanged.Keep journal retry observations buffered until terminal failure (#852)
coneilen commentedon Oct 17, 2026
Confirmation evidence: PR #852 was squash-merged as
77ec22136a99f55e7d0be00ce37979525753a57fat 2026-10-17T01:58:48Z, from exact reviewed R3 headbd6bb43a3850f418a5d9219799d1fbb50944cac8and canonical body SHA-2565173FC06EE6CABB374D6975140F92FBF8AB591EB48040B2556C0CE898539805F.Both required hardening jobs passed on the FIRST attempt, without any diagnostic rerun:
UiCaptureDriver72 executed / 72 passed / 0 failed, plusValidationRunner.Tests.ps1: PASS.ValidationRunner.Tests.ps1: PASS.Each retained whole job log contains exactly one
JournalUnavailablestring: the evidenced PASSing intentionally blocked observer-sink fixture diagnostic. No real/unclassified reader timeout or failing actor-cleanup case appeared in either run. Raw job-log SHA-256: Windows-2022ADD17ABED3531ABAFE6B122914C6BF5D3BF91F4C5A0D5EB88967B0E8B1F12E35; Windows-2025B0EFCD95F83ED05952B2533D163E490D25B94AA567A29F6152E4DC6CD3949FC9.All nine required contexts and all nonrequired checks were accepted. Ready was requested at 01:55:46.4898393Z; a NEW Ready-event DCO run 38992894522, created at 01:55:50Z, passed before merge. The earlier synchronize DCO was not reused as Ready evidence. Final head/body/main/one-commit/five-file/review/CLEAN guards passed; a private compare-response metadata binding was corrected before Ready, without source changes, check bypass or CI rerun.
Persistence was independently checked: merge parent is
95a12681d6d3a6bbc3e1c64992489dd4d04f2863; whole treed057d02a23ed68052a26be61b7530aa747e3a7f8equals the reviewed tree; all eight qualified raw Git blobs and SHA-256 values match on the merged commit. The local R3 contained run separately passed 75/75 with retained lifecycle/journal proofs. Its disclosed concurrent D: deletion remains in the evidence; that local pass was accepted without a replacement or any claim of zero I/O impact.This confirms the terminal-only durability policy on this hosted head, not a universal historical-cause or flakiness claim. Earlier failures remain preserved, including the R2 real reader timeout masked behind #846-shaped actor cleanup; see the recovered-primary record. Whole-observation timing is not isolated FlushFileBuffers timing. No GUI, visual parity, accessibility or macOS safety claim follows.
Leaving #851 OPEN for additional hosted Windows-2025 confirmation, including rebased #850. Closure will be proposed only after the further required runs are green. Next is the separate pre-existing production observer-mask issue, then mechanical #850 rebase, then #849; no live B admission is implied.
coneilen commentedon Oct 17, 2026
Closing as completed. Both hardening jobs passed on the first attempt across two independent heads since the fix, and both fixes are merged:
77ec2213), headbd6bb43a: windows-2022 and windows-2025 both passed 72/72 on the first attempt.206a7dbc), head5a37cd6e: windows-2022 and windows-2025 both passed 85/85 on the first attempt.On both heads, every
JournalUnavailableline in the logs was the expected line from the passing observer-sink fixture; there was no real timeout.Root cause, as confirmed by the artifacts uploaded on failure: journal observation writes called
Flush($true)(FlushFileBuffers) inside the 1000 ms per-object budget. That covered success-path writes and the retryableOpenFailedwrites, and each forced write could take hundreds of milliseconds to about 1 s on hosted runners. #852 starts the budget at the firstOpen, and only terminal observations now flush durably. The budget value and retry semantics are unchanged.Related issues that remain open:
JournalUnavailableis unproven.