Skip to content

fix: promote forwarded exchanges into the live replay index - #9

Merged
JosiahBull merged 1 commit into
mainfrom
jo/fix-empty-file-dedup
Jun 22, 2026
Merged

fix: promote forwarded exchanges into the live replay index#9
JosiahBull merged 1 commit into
mainfrom
jo/fix-empty-file-dedup

Conversation

@JosiahBull

@JosiahBull JosiahBull commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

Bug

"It won't record snapshots if file exists but empty."

The replay index was built once at run() from the snapshot file's initial contents and never updated. Starting from an empty (or partial) snapshot file, a freshly forwarded-and-recorded request never became a replayable snapshot, so a later identical request in the same run missed the index, was forwarded again, and recorded again — duplicating the entry instead of being served from the cache.

Fix

  • ReplaySource is now interior-mutable (a std::sync::RwLock around the exchanges + index; critical sections are short and never .await), with an insert() that adds a freshly recorded Response exchange (errors are never replayed; first-write-wins, matching build_index).
  • After a genuine upstream forward, the lifecycle promotes the recorded exchange into the index so the next identical request is a replay hit.
  • custom_storage_via_per_upstream_snapshots updated to use distinct paths (three identical requests now correctly collapse to a single record).

Together with the skip-on-replay-hit fix in the base PR, this makes Mode::Record a true deduplicating cache (SPECIFICATION.md §8.3/§20.1) even within a single run started from an empty file.

@JosiahBull JosiahBull self-assigned this Jun 21, 2026
@JosiahBull
JosiahBull requested a review from hardbyte June 21, 2026 23:24
@JosiahBull
JosiahBull force-pushed the jo/fix-empty-file-dedup branch from 93bdc63 to a28890d Compare June 22, 2026 00:03
The replay index was built once at `run()` from the snapshot file's
initial contents and never updated. Starting from an empty (or partial)
snapshot file, a freshly forwarded-and-recorded request never became a
replayable snapshot, so a later identical request in the same run missed
the index, was forwarded again, and recorded again — duplicating the
entry instead of being served from the cache.

`ReplaySource` is now interior-mutable (a `std::sync::RwLock` around the
exchanges + index; critical sections are short and never `.await`), with
an `insert()` that adds a freshly recorded `Response` exchange (errors
are never replayed; first write wins, matching `build_index`). After a
genuine upstream forward, the lifecycle promotes the recorded exchange
into the index so the next identical request is a replay hit.

Together with the skip-on-replay-hit fix this makes `Mode::Record` a
true deduplicating cache (SPECIFICATION.md §8.3/§20.1) even within a
single run that started from an empty file.

Also updates custom_storage_via_per_upstream_snapshots to use distinct
paths (three *identical* requests now correctly collapse to one record).

Regression tests:
  record_mode_from_empty_file_dedupes_repeated_request
  record_mode_from_empty_file_still_records_new_request
@JosiahBull
JosiahBull force-pushed the jo/fix-empty-file-dedup branch from a28890d to 1e0597b Compare June 22, 2026 00:15
Base automatically changed from jo/fix-rerecord-replay-hits to main June 22, 2026 00:18
@JosiahBull
JosiahBull marked this pull request as ready for review June 22, 2026 02:55
@JosiahBull
JosiahBull merged commit 0d9c745 into main Jun 22, 2026
3 checks passed
@JosiahBull
JosiahBull deleted the jo/fix-empty-file-dedup branch June 22, 2026 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant