Skip to content

feat(telemetry): add the write-ahead batch cache - #1481

Open
pblazej wants to merge 2 commits into
blaze/telemetry-stack/2-transportfrom
blaze/telemetry-stack/3-cache
Open

pblazej wants to merge 2 commits into
blaze/telemetry-stack/2-transportfrom
blaze/telemetry-stack/3-cache

Conversation

@pblazej

@pblazej pblazej commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The write-ahead cache between the exporter and the transport.

Changes

  • BatchCache with MemoryCache and FileCache (storage_dir)
  • A batch is committed once its file is fsynced and renamed and the directory fsynced
  • Bounded by size and count, every eviction reported
  • A 413 split is replaced in one transaction
  • SpillCache: keeps a batch the disk refused in memory, and takes no write after the opt-out
  • Test-only fault hooks that fail or crash every write, fsync, rename, delete and publish
  • FileCache keeps the newest batch, as MemoryCache does, so a batch larger than the cap still gets its upload attempt
  • An eviction whose directory sync fails still counts the batch once its file is gone
  • remove and clear treat a directory that the OS purged as empty, so an opt-out succeeds; clear also deletes a stray .tmp and reports a delete that fails
  • A push holds the process-wide directory lock while it writes, so a second cache on the same directory never sweeps away its .tmp
  • A rebinding replace with more than one member is rejected before anything is written
Verification

At 1371f6c6, from a clean checkout (CI's test workflow runs only for PRs into main, so these were run locally; there is no clippy job in CI):

  • cargo fmt --check
  • cargo clippy -p livekit-telemetry --all-targets -- -D warnings, also with --all-features and as a cargo hack --feature-powerset --depth 2 run
  • cargo test -p livekit-telemetry: 77 unit, 0 doc
  • RUSTDOCFLAGS="-D warnings" cargo doc -p livekit-telemetry --no-deps reports only the unresolved link to Telemetry::stats in stats.rs, which main (6473ec17) has too; feat(telemetry): add the pipeline, sessions, spans and exporter #1483 adds the item

devin-ai-integration[bot]

This comment was marked as resolved.

@pblazej
pblazej force-pushed the blaze/telemetry-stack/3-cache branch from cf773fd to b969370 Compare October 20, 2026 13:35
`BatchCache` holds encoded batches between the exporter and the transport,
oldest first: `MemoryCache`, or `FileCache` in `storage_dir`, where a batch
is committed once its file is fsynced, renamed and the directory fsynced.
Both evict the oldest batches past their size and count bounds, report
what they evicted, and replace a batch by its two halves in one
transaction. `SpillCache` keeps a batch the disk refused in memory, counted
as a cache write error, and takes no write once the app opted out.
@pblazej
pblazej force-pushed the blaze/telemetry-stack/3-cache branch from b969370 to a06da21 Compare October 23, 2026 06:09

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

Comment on lines +446 to +447
if self.remove(id).is_ok() || matches!(self.path(id, BATCH_EXT).try_exists(), Ok(false))
{

@devin-ai-integration devin-ai-integration Bot Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Unsynced evictions count as permanent losses

When directory sync fails after an unlink, prune reports the batch as evicted because its file is absent. A crash can restore the unsynced file, so the exporter counts a batch as lost before replaying it.

Learn more

The cache treats a directory sync as the durability boundary for both writes and deletes. remove can return an error after unlinking a file if that sync fails. A missing path then proves only that the unlink is visible now, not that it survives a crash. Reporting the ID to the exporter as evicted records a loss that can be contradicted when the file returns on restart.

Example: With a 10-byte cap, pushing batch B evicts batch A. Removing A unlinks its file, but directory sync fails; try_exists returns false and A is reported as dropped. A power loss before another successful directory sync can bring A back for upload.

Recommended fix: Distinguish an unlink that succeeded from a durable deletion. Retry the directory sync when possible, or defer final eviction reporting until durability is confirmed; preserve the failed ID for recovery or subsequent accounting.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Counting the visible removal is deliberate, even when the directory sync after it fails, and an_eviction_whose_directory_sync_fails_still_counts pins it. If a power loss brings the file back, the next launch either sends it or evicts it and counts it again, so the loss count can be one batch too high, but no record is sent twice. The cache.rs docs now say that the counter does not promise a crash-safe delete (1371f6c).

…ectory

`FileCache::prune` now keeps the newest batch, as `MemoryCache` does, so
a batch larger than the cap still gets its upload attempt. An eviction
whose directory sync fails still counts the batch once its file is gone,
so the batch just committed is no longer evicted with it. `remove` and
`clear` treat a directory that the OS purged as empty, so an opt-out
reports success. A push now holds the process-wide directory lock
(`DIR_LOCK`, was `SPLITS`) while it writes, so a second cache opening on
the directory cannot sweep away its `.tmp`. A multi-member rebinding
`replace` is rejected before anything is written, because recovery could
not tell it apart from a finished one. `clear` also deletes a `.tmp`
that a failed write could not remove, under the directory lock, and
reports a delete that fails. The file cache's diagnostics wait until the
spill gate is released, so a host logger never runs under it.

The spill warning fires once per failure episode instead of once per
pipeline, and it and the split warning are logged after their locks.
`clear` lets a stuck journal be retried. Push and replace share one
after-write step that logs a prune error. The file extension and journal
separator constants are named and documented, the module-wide dead-code
expectation narrows to `SpillCache`, and the docs state the real id
format and failure behavior. A changeset records the stage. The
`BatchCache` docs list the characters an id holds, all of them valid in
a file name on every platform. The `MemoryCache` docs name the
newest-batch exception to its byte bound. The `BatchCache` and `prune`
docs say that an eviction counts once its file is gone, even when the
directory sync after the unlink failed: the count follows the visible
removal and does not promise a delete that survives a crash.
@pblazej
pblazej force-pushed the blaze/telemetry-stack/3-cache branch from a06da21 to 1371f6c Compare October 23, 2026 09:56

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal to tag changes that don't require changelog documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant