# Journal buffers, I/O faults and checkpoint scheduling — 2026-10-09

Session: `codex-review-edges-1009`, based on `e6e755fa0` in private
`atrekes/libhft-devel`. Parallel C++, Java and Rust reviewers worked in the
session worktree; root alone executed builds/tests, serialized under the shared
host lease and capped at 16 jobs. Initial execution waited for existing host
builds/sweeps. All products used the session's `LIBHFT_BUILD_ROOT`.

## Aliased replay buffers

Java `FileStore.append` staged input into a replay slot and then queued another
copy from the original input array. A borrowed `frame()` subrange can overlap
that same slot; staging changes the second copy's source. RAM replay therefore
looked correct while the journal held different bytes. Acknowledging the queued
record cleared the dirty marker, allowing that difference to survive sync,
close and reopen. Public FIXP `get`/`save` exposes the same case.

The Java queue now takes the completed staged bytes. C++ classic and FIXP file
stores had the same two-copy ordering problem and now queue their staged payloads
too. C++ memory/fixed/file staging uses `memmove` for overlapping replay subviews.
The direct C++ `JournaledStore::SaveOutbound` decorator records input before its
backing store mutates it. Both destinations are still attempted independently,
with the same combined success and absent-destination behavior. Its separate
durable preparation helper retains backing-commit-before-audit ordering.

No extra payload copy, allocation, lookup or lock is introduced. The ordinary
asynchronous save paths remain free of added file I/O. The alias correction is
not presented as a measured latency improvement.

## C++ buffered journal faults

The binary journal did not latch failed `fflush`; the text journal could continue
after failed writes or flushes. Buffered records accepted by `fwrite` were also
mistaken for physical rollback boundaries. A subsequent failure could leave a
torn prefix, or truncate/extend to bytes that had never fully reached the file.

Both journals now latch write, flush and close failures until explicit reopen.
They separately track accepted and successfully flushed boundaries. Cold fault
cleanup duplicates the descriptor, closes stdio, then checks actual file size
and truncates to a safe boundary. Closing first prevents a later stdio flush from
re-appending behind the rollback. Rollback remains best effort if descriptor
copying, size lookup or truncation fails; the stream is still retired. Nonregular
streams are closed and faulted without pretending to repair their contents.
Successful Append adds no new syscall. Flush here means stdio flush, not fsync.

## Rust FIXP checkpoint scheduling

FIXP checkpoints waited in a polling loop for the background writer, which can
be sleeping when the queue becomes nonempty. They now reuse the classic store's
existing owner-side queue drain under the writer mutex. The worker rechecks its
captured queue head under that mutex, so either consumer can safely finish a
record. Queue order, evicted queued payloads, dropped retained payloads/checkpoints,
and finalized session IDs remain intact. Obsolete settle/sequence-list helpers
were removed. Ordinary asynchronous saves are unchanged.

Four tests hold the worker at an observed-empty queue and require checkpoint or
finalization to complete before releasing it. The eight final combinations cover
two ring capacities and payload/empty-save endings, then resume writes and reopen.
This establishes removal of worker scheduling dependence at the synchronous
boundary; no NIC benchmark or latency percentile was measured.

## Reproduction and focused verification

- C++ journal: original header fails 18 of 22 cases; four healthy controls pass.
  Corrected optimized and AddressSanitizer/UndefinedBehaviorSanitizer runs pass
  all 22, with leak detection and fatal UB enabled.
- C++ alias: four deterministic 12-case original-code families each have four
  failures (classic/FIXP queued-close and binary/text decorator). Nine composition
  controls pass. Two separate original sanitized probes diagnose overlapping
  memcpy in the memory stores. All 129 final cases pass optimized and sanitized.
  Sync may discard queued copies, so no fixed failing baseline count is asserted
  for that race-dependent old path. Independent raw journal decoding checks every
  candidate record, avoiding a later same-key rewrite masking an earlier error.
- Java: original code with the new class has eight assertion failures and 64
  controls; the mandatory native-library guard also passes. The final focused
  gate passes 230 unique cases, including all 72 new cases, without failures,
  errors or skips. Ring-full, disjoint and external input controls are included.
- Rust: the four new methods fail on the original checkpoint implementation at
  the parked-worker assertion. A failing method stops at its first ring variant;
  the eight final variants are not counted as eight original failures. The final
  store-focused selection passes 83 tests.

## Full verification

C++11 open edition passes 149/149 binaries with zero reported skips, including
layering and simulator checks. The edition selection excludes 83 private binaries;
one additional private mmap/journal binary was explicitly built and passes,
including durable preparation ordering and the existing short-write fixture.

Java development passes 5,969 unique cases; open passes 5,526. Both have zero final
failures, errors or skips, with mandatory native loading and separately rebuilt
native libraries. The three counters-disabled cases skip in the ordinary fork
and pass in their configured fork; counts deduplicate by class and test name.
The development/open totals include all 72 new alias cases each.

Rust default and no-default-features each pass 1,441 tests, with the same one
pre-existing builder documentation example ignored in each. After rebasing onto
`458b05777`, both full feature runs pass again with the same counts. The incoming
Rust changes combine equivalent header-walk branches and clean up test expressions;
all thirteen reviewed engine-file hashes are unchanged. Strict Clippy on all targets
also passes with warnings denied. The final source manifest includes those thirteen
files plus the two incoming Rust context files; all fifteen pass the export scan.

Independent cross-reviews found no blocking issue in alias handling, queue-head
ownership, journal rollback or decorator ordering. Root caught and corrected a
missing C++ test link rule before executing the new target. Linux execution only;
Windows-specific journal branches were not run. The new journal binary explicitly
reports its POSIX and Linux fixture skips on other platforms.

## Reserved .NET counterpart

The same staged-then-original copy exists in .NET `FileStore.TryAppend`, in
`engine/dotnet/src/Libhft/MemoryStore.cs`. Its source and test paths remain reserved
by the .NET structure owner in `docs/WIP_AGENT_CLAIMS.md`. A prepared minimal fix
queues `_messages[slot].AsSpan(0, message.Length)`.

A standalone project compiles byte-identical original/proposed store snapshots,
the actual supporting store classes and `TestTempPaths`, plus 24 public-API alias
cases. Original: four failures and 20 controls; proposed: all 24 pass. It checks
RAM, journal payload, close/sync, reopen and recovered sequence state. This is a
managed-store proof; full .NET/native integration is pending.

An explicit reserved-path exception was requested after the proof passed and
has not been received. No .NET change is applied on main by this review. The
verified patch is retained in the archive and on private proposal branch
`s/codex-review-edges-1009-dotnet-proposal`, commit
`9d7765a3ff0bfdb9ad33c4ace3c2aa249d63c2b6`. Its two Git blobs are checked against the
exact proposed source and regression that ran in the standalone proof. It was
prepared using a separate Git index; the reserved worktree paths were not edited.

## Evidence and publication

Publication targets private `atrekes/libhft-devel` directly after final fetch,
rebase and comparison with the exact tested source hashes. Other sessions' claims
are preserved. Final counts are retained in `verification-summary.json`.

Evidence is retained locally and on HP under
`~/archive/libhft/reviews/2026-10-09-codex-review-edges-1009/`, including original/final
sources, counterfactual drivers, complete test reports, source hashes, proposed
.NET patch and standalone proof, publication patch and history bundle. Both
archive manifests are checked before deleting this session's worktree/build root.
