Files
kenn-io--agentsview/docs/internal/background-sync-efficiency.md
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

82 lines
4.3 KiB
Markdown

# Background Sync Efficiency
This document records the runtime and cost-model contracts that keep active
session writers from turning into unbounded background work. The implementation
lives primarily in `internal/sync/watcher.go`,
`internal/parser/codex_cursor.go`, and the provider incremental path in
`internal/sync/engine.go`.
## Watcher runtime contract
- Production watcher events use a 500 ms first-event batching window. Later
events join the pending set without postponing its deadline.
- Watcher callback start times are at least five seconds apart. One worker runs
callbacks serially while the fsnotify loop continues draining events and
errors, so a long sync cannot block event intake or overlap another sync.
- An idle watcher has no running timer or ticker. The first relevant event
creates the next one-shot timer.
- Each pending or in-flight batch retains at most 8,192 unique paths and 2 MiB
of path-string bytes. At most one batch is in flight while one more
accumulates. Entry count separately bounds map and slice overhead.
- Exceeding either batch limit replaces its individual paths with one explicit
full-sync marker. The worker clears event-sensitive freshness caches and
force-verifies every discovered file under the same serialization,
dispatch-floor, cancellation, and shutdown rules, so overflow bounds memory
without losing same-stat source changes.
- Shutdown discards pending paths and waits only for an already-running
callback. Normal discovery on the next startup recovers discarded changes.
## Codex append cursor contract
The Codex provider factory owns one in-memory cursor cache shared by its
per-source provider instances. Its lifetime is the sync engine's lifetime; it is
not persisted across daemon restarts.
Each cursor is keyed by the cleaned physical path, exact safe byte offset, and
inode/device identity where the platform exposes them. The cache is an LRU
bounded to 256 entries and 2 MiB of estimated retained data. It contains compact
continuation state only: never parsed messages, raw JSON lines, complete prompt
bodies, file contents, or open file descriptors.
The database's committed source offset is the cursor commit token. A parse may
stage old- and new-offset entries, but only an exact offset from the next
database request is eligible. A failed database write therefore retries from the
old cursor; an unreachable staged entry is eventually evicted.
Every nonzero resume offset must immediately follow a newline. Incremental
parsing commits only complete, valid, newline-terminated JSONL records. Partial
records and valid JSON at a newline-less EOF are retried or force a full parse;
they are never published as safe cursor boundaries.
Truncation, known file-identity replacement, manual or project refreshes,
`session_index.jsonl` title changes, and records that retroactively update
stored messages all fall back to an authoritative full replacement. Safe
incremental writes preserve the index-folded mtime and lifecycle-derived
termination status alongside message and token aggregates.
## Append-only limitation
Cursor correctness assumes that growth is append-only. A same-inode file can
grow after bytes inside its already-committed prefix have been rewritten. Size,
identity, and boundary checks do not detect that case, and the current
full-source fingerprint is not compared with a separately verified stored prefix
before incremental parsing. Closing this gap would require rolling hash state or
explicit prefix verification and remains deferred.
## Cost model and regression evidence
A warm Codex cursor makes continuation-state parsing scale with appended records
rather than transcript history. End-to-end append sync is still O(file): the
provider's `Fingerprint` hashes the complete source and the engine's
`ComputeFileHashPrefix` hashes through the newly committed offset.
- `BenchmarkCodexIncrementalCursor` in `internal/parser` compares cold prefix
reconstruction with the exact warm cursor. It is diagnostic because
`internal/parser` is not in `BENCH_GATE_PACKAGES`.
- `BenchmarkCodexIncrementalSyncReads` in `internal/sync` measures the warm tail
between the two remaining linear reads. It is PR-gated because
`internal/sync` is in `BENCH_GATE_PACKAGES`.
The maintained behavioral gate inventory is in
[Performance Gates](performance-gates.md).