fix(ipns): prefer DB cache over stale network IPNS records#203
Conversation
resolveRecord() now compares sequence numbers from both the network (delegated-ipfs.dev) and DB cache, returning whichever is newer. The DB is always up-to-date (written synchronously during publish) while the network resolver can serve stale cached records — causing post-reload sync to return old metadata on staging. Also increases E2E test timeouts and adds retry logic for staging IPNS latency in the page-reload and shared-folder-visibility tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Entire-Checkpoint: 17ec0a7a46d4
WalkthroughThe PR implements sequence number comparison in IPNS resolution to prefer the fresher data source between network and database cache, adds corresponding unit tests, documents staging environment issues, and updates E2E tests with extended timeouts and retry mechanisms to handle IPNS propagation delays on staging. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 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 (2)
apps/api/src/ipns/ipns.service.ts (1)
387-398: Consider handling corrupted sequence numbers defensively.
BigInt(result.sequenceNumber)andBigInt(cached.sequenceNumber)will throw aSyntaxErrorif either value is an empty string or non-numeric. While both values are controlled (network defaults to'0', DB starts at'0'), a corrupted DB row could crash the resolve path.A
try/catcharound the BigInt comparison that falls back to the network result would make this more resilient.🛡️ Defensive BigInt comparison
if (result && cached?.latestCid) { // Both sources available — prefer the one with the higher sequence number - const networkSeq = BigInt(result.sequenceNumber); - const dbSeq = BigInt(cached.sequenceNumber); - if (dbSeq > networkSeq) { - this.logger.log( - `DB cache has newer sequence (${dbSeq} > ${networkSeq}) for ${ipnsName}, using DB: ${cached.latestCid}` - ); - return { cid: cached.latestCid, sequenceNumber: cached.sequenceNumber }; + try { + const networkSeq = BigInt(result.sequenceNumber); + const dbSeq = BigInt(cached.sequenceNumber); + if (dbSeq > networkSeq) { + this.logger.log( + `DB cache has newer sequence (${dbSeq} > ${networkSeq}) for ${ipnsName}, using DB: ${cached.latestCid}` + ); + return { cid: cached.latestCid, sequenceNumber: cached.sequenceNumber }; + } + } catch { + this.logger.warn( + `Failed to compare sequence numbers for ${ipnsName}, using network result` + ); } return result; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/src/ipns/ipns.service.ts` around lines 387 - 398, The BigInt conversions of result.sequenceNumber and cached.sequenceNumber can throw on corrupted/non-numeric strings; wrap the comparison logic (the block that creates networkSeq and dbSeq and compares them) in a try/catch inside the resolve path so that any error during BigInt(result.sequenceNumber) or BigInt(cached.sequenceNumber) is caught and you fall back to returning result (the network value). Keep the existing log when DB is newer, but in the catch log a warning including ipnsName and the offending cached.sequenceNumber/value, and then return result to avoid crashing the resolve flow.apps/api/src/ipns/ipns.service.spec.ts (1)
900-949: Good coverage of the two primary comparison branches.The two new tests correctly validate DB-preferred and network-preferred paths. One minor gap: there's no test where network sequence is strictly greater than DB (e.g., network=15, DB=10). While the implementation handles it via the same code path as the equal case, an explicit test would document the expected behavior and prevent regressions if the comparison operator changes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/api/src/ipns/ipns.service.spec.ts` around lines 900 - 949, Add a third unit test in ipns.service.spec.ts that exercises service.resolveRecord when the network IPNS record has a strictly greater sequence than the DB record (e.g., network sequence 15 vs DB sequence '10') to ensure the network result is chosen; reuse the same mocks (mockFetch, mockParseIpnsRecord, mockFolderIpnsRepo.findOne) but set mockParseIpnsRecord to return sequence 15n and mockFolderIpnsRepo.findOne to return sequenceNumber '10', then assert result is not null and that result.cid and result.sequenceNumber match the network values (verifying resolveRecord prefers the higher network sequence).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/api/src/ipns/ipns.service.spec.ts`:
- Around line 900-949: Add a third unit test in ipns.service.spec.ts that
exercises service.resolveRecord when the network IPNS record has a strictly
greater sequence than the DB record (e.g., network sequence 15 vs DB sequence
'10') to ensure the network result is chosen; reuse the same mocks (mockFetch,
mockParseIpnsRecord, mockFolderIpnsRepo.findOne) but set mockParseIpnsRecord to
return sequence 15n and mockFolderIpnsRepo.findOne to return sequenceNumber
'10', then assert result is not null and that result.cid and
result.sequenceNumber match the network values (verifying resolveRecord prefers
the higher network sequence).
In `@apps/api/src/ipns/ipns.service.ts`:
- Around line 387-398: The BigInt conversions of result.sequenceNumber and
cached.sequenceNumber can throw on corrupted/non-numeric strings; wrap the
comparison logic (the block that creates networkSeq and dbSeq and compares them)
in a try/catch inside the resolve path so that any error during
BigInt(result.sequenceNumber) or BigInt(cached.sequenceNumber) is caught and you
fall back to returning result (the network value). Keep the existing log when DB
is newer, but in the catch log a warning including ipnsName and the offending
cached.sequenceNumber/value, and then return result to avoid crashing the
resolve flow.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.learnings/2026-02-25-ipns-stale-resolution-staging.mdapps/api/src/ipns/ipns.service.spec.tsapps/api/src/ipns/ipns.service.tstests/e2e/tests/full-workflow.spec.tstests/e2e/tests/sharing-workflow.spec.ts
Summary
resolveRecord()now compares sequence numbers from both the delegated routing network and DB cache, preferring whichever is newer. The DB is always authoritative (written synchronously during publish) whiledelegated-ipfs.devcan serve stale cached records — this caused post-reload IPNS sync to return old metadata on staging.Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests
Documentation