Skip to content

fix: preserve replay batch identity - #4

Merged
kzekiue merged 1 commit into
mainfrom
fix/ingest-lifecycle-ordering
Jul 10, 2026
Merged

fix: preserve replay batch identity#4
kzekiue merged 1 commit into
mainfrom
fix/ingest-lifecycle-ordering

Conversation

@kzekiue

@kzekiue kzekiue commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

Adds explicit delivery and recorder-lifecycle identities to ingest batches so retries are idempotent and replay order remains deterministic across reloads and recorder restarts.

Problem

Using (session_id, seq) as a retry identity is unsafe because seq is a local counter. A full-page reload or stop/init cycle creates a new recorder lifecycle and restarts seq at zero, so the same session can legitimately contain multiple seq: 0 batches. Treating those as duplicates would discard valid recordings; accepting all of them without a delivery identity allows retries to duplicate stored events and counters.

Solution

  • batchId is a stable delivery identity for one batch and is reused by transport retries.
  • recordingInstanceId identifies a recorder lifecycle.
  • recordingOrder explicitly orders recorder lifecycles within a session.
  • seq orders batches only within one recorder lifecycle and resets for a newly started lifecycle.
  • The database ignores only duplicate (session_id, batch_id) conflicts; other database failures propagate.
  • Replay reads batches by recording_order, seq, recording_instance_id, and row id for deterministic ties.
  • Session URL, viewport, user agent, and lifecycle start metadata are owned by the highest accepted lifecycle tuple, so delayed older batches cannot roll them backward.
  • ended_at is monotonic and records the latest observed activity time.
  • Legacy payloads remain accepted with documented synthetic identities.

Migration

  • V4 rebuilds events with non-null batch_id, recording_instance_id, and recording_order, backfills historical rows with deterministic legacy identities, and adds the unique (session_id, batch_id) index.
  • V5 adds the lifecycle owner fields for session summary metadata and backfills them from the highest retained recorder lifecycle.
  • Existing databases migrate transactionally and retain all historical event rows.
  • The V4 rebuild holds a write lock and can need substantial temporary database/WAL disk space. It recreates TinyReplay-managed event indexes. SQLite drops table-bound indexes and triggers during the rebuild; unknown out-of-band schema objects are intentionally unsupported and are not preserved.

Testing

  • Retry idempotency and targeted non-idempotency error propagation.
  • Full-page reload and stop/init lifecycle resets.
  • Multiple batches from one recorder retain lifecycle identity/order, advance sequence, and use distinct delivery ids.
  • Delayed and out-of-order batches preserve replay ordering, lifecycle-owned metadata, and monotonic ended_at.
  • V4/V5 migration backfill, index recreation, and unsupported custom schema-object behavior.
  • Legacy/new-client ordering and metadata ownership.
  • Partial new-protocol payload validation.

Backward Compatibility

Legacy SDK payloads without all three new protocol fields are accepted. The server assigns a random delivery id, legacy-<sessionId> recorder identity, and recording order 0; therefore legacy batches replay before new SDK lifecycles, which begin at order 1, in a shared session. Legacy clients do not gain retry deduplication because they cannot supply a stable batchId. New-client semantics remain isolated by their explicit lifecycle ordering and stable delivery identity.

Checklist

  • All tests pass
  • Lint passes
  • Typecheck passes
  • Production build passes
  • Documentation updated

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tinyreplay-docs Ready Ready Preview, Comment Jul 10, 2026 9:53pm

@kzekiue
kzekiue merged commit da8415a into main Jul 10, 2026
5 checks passed
@kzekiue
kzekiue deleted the fix/ingest-lifecycle-ordering branch July 14, 2026 06:29
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