Repository navigation
SDK/mount: content-hash client cache (If-None-Match) + jittered Retry-After on all 429 paths - #519
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Relayfile Eval ReviewRun: Passed: 4 | Needs human: 0 | Reviewable: 0 | Missing output: 0 | Failed: 0 | Skipped: 0 Human Review CasesNo reviewable human-review cases captured Relayfile output. |
Address review feedback on the client content-hash caches: - SDK (TS + Python): dedupe only content bytes by (hash, encoding); keep revision/path/semantics per cache key so two paths with identical bytes never swap metadata on a 304. - SDK (TS): never store hashless responses; they cannot be revalidated with If-None-Match, so they always go to the server instead of being served from cache indefinitely. - mount: the persistent object store now holds raw bytes only (no path, revision or content type from the workspace that first fetched them) and is byte-capped (1 GiB default) with LRU eviction by mtime. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The server ETag for /fs/file is opaque and may encode more than the content hash, so building If-None-Match from contentHash never revalidates. Both SDKs now store the exact ETag the server returned for each path (refreshed from 304 responses) and echo it verbatim in If-None-Match. The local byte store stays keyed by the response body's contentHash. Responses without an ETag are not cached, since they cannot be revalidated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit be39be3. Configure here.
put() invalidated the path under the lock, released it to validate the body, then re-inserted. A concurrent put for the same path could leave a stale object reference whose later LRU eviction dropped the live path. Validate outside the lock, then invalidate + insert under one lock hold; eviction now only drops paths that still point at the evicted object. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

PR summary
What changed
If-None-MatchusingcontentHashand serve304responses locally; caching can be disabled.~/.relayfile/cache/objects/<sha256>. Authorized bootstrap tree hashes are checked before body reads, so later mounts can materialize identical content without transferring it again.Retry-Afteras a minimum, including HTTP-date parsing already supported by each client.relayfile listenretry initial 429/503 handshakes, retain the last processed event cursor, and reconnect from that cursor with full jitter. Applied the same handshakeRetry-After/jitter behavior to mount and FUSE WebSocket reconnects.details.reasonpropagation to GoHTTPError, updated SDK parity metadata, tests, and configuration/changelog documentation.Scope decisions
internal/mountfuse/wsinvalidate.gobecause it is a shipped/fs/wsreconnect loop with the same fleet lockstep risk.packages/agentsreconnects, mount outbox writes, and CLI polite polling: they are one-shot, write-path, or polling flows rather than the file-read fan-in and event reconnect paths addressed here.Validation
client.test.tsassertions pass; one pre-existing environment assertion expects Node to lack globalErrorEvent, which is false on the installed Node 25 runtime.internal/mountsync,internal/mountfuse, andcmd/relayfile-cli.scripts/check-contract-surface.shpasses, including SDK parity.PATH; unrelated launcher timing tests remain environment-sensitive.Contract note
The clients deliberately use the response body's
contentHashfor object identity. The currently documented serverETagis a revision identifier; the separate server change must redefine it to the quoted content hash and addIf-None-Match/304handling before conditional requests can save network bodies against older servers.Checks
Relayflow ran this repository's checks (.relayflow/check.sh) and they passed.
What ran (.relayflow/check.sh)
Fixes #518
Note
Medium Risk
Touches default read/retry behavior and mount bootstrap concurrency fleet-wide; correctness depends on ETag/
304server support for SDK conditional reads, though mount object reuse is hash-verified locally.Overview
Adds content-addressed read caching and polite overload handling across SDKs, mounts, and long-lived WebSocket listeners.
SDKs (TypeScript & Python): Replaces the old TTL/entry-count read cache with a byte-capped LRU keyed by
contentHash, with per-path metadata kept separate. Cached reads send the server’s opaqueETaginIf-None-Match, handle304, and skip caching whenETagorcontentHashis missing. Retries now use full jitter withRetry-After(and body hints where applicable) as a minimum delay rather than capping it at the client backoff.Mount sync (
internal/mountsync): Introduces a verified on-disk object store at~/.relayfile/cache/objects/<sha256>(1 GiB LRU, bytes only). Bootstrap/incremental reads skip network bodies when the tree entry’s hash is already local, while path/revision still come from the current mount’s tree. Bootstrap and incremental read concurrency drop from 16 (env max 64) to a hard cap of 4. HTTP errors can surfacedetails.reasononHTTPError.Reconnect paths:
relayfile listenkeeps the last event cursor and reconnects withcursor=on the dial URL; initial 429/503 handshakes retry instead of failing immediately. FUSE/mount WebSocket invalidators parse dialRetry-Afterand use the same jitter pattern. Syncer WebSocket reconnect delay switches to full jitter.Mount launcher: Foreground
--oncekeeps polling mount readiness until the timeout after exit 0, avoiding a race where state isn’t visible yet.Docs, parity metadata (
client-read-cache→ both), changelogs, and tests accompany the behavior changes. Trajectory index JSON under.trajectories/is also updated.Reviewed by Cursor Bugbot for commit 6364f3f. Bugbot is set up for automated code reviews on this repo. Configure here.