feat(manifest): faster + more correct manifest path resolution (cache, index precedence, id validation) - #832
Conversation
Two correctness fixes in the streaming manifest parser (resolveManifestStreamPath): - Defer index resolution until the `index` object closes so `index.id` deterministically wins over `index.path`, regardless of JSON key order. Previously a v0.2.0 manifest whose `paths` preceded `index` (with `path` before `id` inside `index`) resolved to the id `index.path` mapped to, not `index.id` — so the served index could differ from other gateways. - Reject data ids that are not 43-char base64url before resolving them, avoiding data-retrieval attempts on malformed manifest entries. Adds regression fixtures/tests for both. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPbcF2M3P1XcM8mU8FWEf9
…uests A manifest transaction is immutable, so a resolved (manifest id, path) -> data id mapping is valid forever. StreamingManifestPathResolver now keeps a bounded LRU of resolutions: resolveFromData populates it, resolveFromIndex serves hits with `complete: true` so the caller skips fetching and re-parsing the manifest body entirely. Every ArNS site is a manifest, and each asset on a page is a separate path resolution against the same manifest, so the current code re-parses the whole body per asset. On a real 7,784-path (500 KB) manifest, resolution drops from ~57 ms to a sub-ms cache lookup (~190x on repeat). Size configurable via MANIFEST_RESOLUTION_CACHE_SIZE (default 5000). Positive and negative resolutions are both cached (immutable per tx). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPbcF2M3P1XcM8mU8FWEf9
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughManifest parsing now validates IDs and applies order-independent index precedence. Streaming manifest path resolution adds bounded caching for successful and unsuccessful results. Cache capacity is configurable through environment settings and system wiring. ChangesManifest resolution
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant StreamingManifestPathResolver
participant LRUCache
participant ManifestBody
Client->>StreamingManifestPathResolver: resolve manifest path
StreamingManifestPathResolver->>LRUCache: look up manifest ID and normalized path
alt cache hit
LRUCache-->>StreamingManifestPathResolver: return cached result
else cache miss
StreamingManifestPathResolver->>ManifestBody: resolve path from manifest data
ManifestBody-->>StreamingManifestPathResolver: return positive or negative result
StreamingManifestPathResolver->>LRUCache: store result
end
StreamingManifestPathResolver-->>Client: return resolution
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/encoding.ts (1)
227-232: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd TSDoc for the new helper and method.
The touched TypeScript APIs use line comments instead of TSDoc.
src/lib/encoding.ts#L227-L232: add TSDoc forisValidManifestId.src/resolution/streaming-manifest-path-resolver.ts#L35-L39: add TSDoc forcacheKey.As per coding guidelines, “Add or improve TSDoc comments on code you touch.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/encoding.ts` around lines 227 - 232, Add concise TSDoc describing the purpose and validation behavior of isValidManifestId in src/lib/encoding.ts at lines 227-232, and add TSDoc describing the cacheKey API in src/resolution/streaming-manifest-path-resolver.ts at lines 35-39; update both documented symbols without changing their implementation.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/lib/encoding.ts`:
- Around line 227-232: Add concise TSDoc describing the purpose and validation
behavior of isValidManifestId in src/lib/encoding.ts at lines 227-232, and add
TSDoc describing the cacheKey API in
src/resolution/streaming-manifest-path-resolver.ts at lines 35-39; update both
documented symbols without changing their implementation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: fe3a286b-c70a-48c4-a00f-a621ea265f31
📒 Files selected for processing (11)
docker-compose.yamldocs/envs.mdsrc/config.tssrc/lib/encoding.test.tssrc/lib/encoding.tssrc/resolution/streaming-manifest-path-resolver.test.tssrc/resolution/streaming-manifest-path-resolver.tssrc/system.tstest/mock_files/manifests/example_manifest_invalid_ids.jsontest/mock_files/manifests/example_manifest_v0.2.0_paths_before_index_path_first.jsontest/stubs.ts
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #832 +/- ##
===========================================
+ Coverage 78.77% 78.85% +0.07%
===========================================
Files 133 134 +1
Lines 50969 51113 +144
Branches 3847 3858 +11
===========================================
+ Hits 40152 40303 +151
+ Misses 10765 10758 -7
Partials 52 52 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Addresses CodeRabbit review on #832. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPbcF2M3P1XcM8mU8FWEf9
Addresses CodeRabbit review on #832. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPbcF2M3P1XcM8mU8FWEf9
…parsing Implements the previously-stubbed resolveFromIndex against a persistent, lazily-populated store, so a manifest's root/index survives restarts and can be served even when the manifest body is momentarily unreachable upstream. - New `manifest_resolutions` table (data.db) keyed by manifest tx id, storing index_id / fallback_id. Dedicated table — no stable_* / auto-verify / flush entanglement. Migration is additive with a reversing down migration. - New DB methods getManifestResolution (read) / saveManifestResolution (upsert, COALESCE-preserving) on the 'data' worker pool, exposed via the ManifestResolutionStore interface. - StreamingManifestPathResolver now consults the store for the root only (index/fallback — the highest-traffic case), and lazily persists the root resolution after resolveFromData. Sub-paths are never stored (the index has no path map) and continue to be served from the in-memory cache (PR #832). Persistence is fire-and-forget and store failures never break serving. Values are immutable per manifest transaction, so no invalidation is needed. Tests: real-SQLite round-trip incl. COALESCE upsert; resolver store hits for index/fallback, sub-path skip, lazy persist, store-failure fallthrough, store-optional. standalone-sqlite suite unaffected (69/69). Stacked on #832 (feat/manifest-resolution-improvements). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPbcF2M3P1XcM8mU8FWEf9
Draft — PR 1 of 2 in the manifest-resolution work (see follow-up for the persistent
resolveFromIndexindex). No schema changes here.What & why
Every ArNS site is an Arweave manifest (a map of URL paths → data ids), and each asset on a page is a separate path resolution against the same manifest. Today the gateway streams and re-parses the entire manifest body on every resolution. This PR removes that redundant work and fixes two correctness issues, all within the streaming resolver — no DB/migration.
Changes
perf: cache resolved manifest paths (1c).A manifest tx is immutable, so a resolved
(manifest id, path) → data idmapping is valid forever.StreamingManifestPathResolvernow keeps a bounded LRU:resolveFromDatapopulates it;resolveFromIndexserves hits withcomplete: true, so the caller (sendManifestResponse) skips fetching and re-parsing the body. Positive and negative resolutions are both cached. Size viaMANIFEST_RESOLUTION_CACHE_SIZE(default 5000).fix: deterministic v0.2.0 index precedence (1a).Index resolution is deferred until the
indexobject closes, soindex.iddeterministically wins overindex.pathregardless of JSON key order. Previously a manifest whosepathsprecededindex(withpathbeforeid) resolved to the idindex.pathmapped to — meaning the served index could differ from other gateways (a cross-gateway consistency bug). Depth guard excludes apathsentry keyed"index".fix: reject malformed data ids (1b).Manifest entries whose id isn't 43-char base64url are rejected before a data-retrieval attempt.
Before/after (real manifests, 200 repeat resolutions, body in memory → isolates parse cost)
The large real manifest costs ~57 ms of CPU parse per resolution today — a 30-asset homepage burns ~1.7 s of pure parse CPU per view, eliminated on cache hits. When the body is not locally cached, the current path additionally pays a full upstream fetch per resolution (not measured here) — the follow-up persistent index removes that too.
Tests
src/lib/encoding.test.ts: +4 (deterministic index-id precedence regression; malformed-id rejection). 29/29 pass.src/resolution/streaming-manifest-path-resolver.test.ts(new): cache hit/miss, negative caching, trailing-slash key normalization, per-manifest keying. 5/5 pass.lint:checkclean.Docs
MANIFEST_RESOLUTION_CACHE_SIZEadded todocs/envs.mdanddocker-compose.yaml.Not in this PR
1eobservability metrics (small follow-up).1dpersistentmanifest_index_id/manifest_fallback_idindex implementingresolveFromIndexfrom a dedicated table (separate PR — carries the migration).🤖 Generated with Claude Code
https://claude.ai/code/session_01LPbcF2M3P1XcM8mU8FWEf9