fix(AAIDomains): separate Supabase fetch retry from onPage callback - #1029
Conversation
The retry loop was catching both Supabase fetch errors AND onPage (DB save) errors. A failed DB insert would cause _executeLoad to re-fetch the same Supabase page and retry the save, potentially creating duplicate chunks. Restructure _executeLoad so the retry loop covers the Supabase fetch only. onPage is called after a successful fetch — if it throws, the error propagates up to the caller without retrying.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
PR Review posted below as separate comments due to formatting constraints. |
|
PR Review - fix(AAIDomains): separate Supabase fetch retry from onPage callback. Full review below. |
|
SUMMARY: This PR fixes a correctness bug in _executeLoad where the retry loop wrapped both the Supabase fetch and the onPage callback. A failed DB save caused the same page to be re-fetched and re-processed, potentially producing duplicate document chunks. The fix narrows the retry scope to the Supabase fetch only so onPage errors propagate to the caller without triggering retries. |
|
CRITICAL ISSUES: None identified. |
|
MAJOR CONCERN 1 - Silent iteration when all pages are filtered out by tag logic (AAIDomains.ts lines 569-596): After a successful fetch, pageData may be non-empty but filteredDomains can still be empty after tag filtering. fetchedDomainCount is not incremented in that case. The outer while loop condition (fetchedDomainCount < this.limit) means if every page is fully filtered out, the loop only terminates when Supabase returns a short page or no data. The lastId cursor is correctly advanced from the unfiltered pageData regardless, so pagination is safe. This is not a regression - the same behaviour existed before - but the refactor makes this path more prominent and worth noting. |
|
MAJOR CONCERN 2 - Dead-code guard at line 567: The check [if (retryCount >= maxRetries) break] is effectively unreachable. When all retries are exhausted the inner while loop unconditionally throws lastError (line 562), so execution never arrives at line 567. It would only activate if maxRetries were 0, but it is hardcoded to 3. Consider removing it or adding a comment clarifying it is a purely defensive guard, to avoid misleading future readers. |
|
MINOR SUGGESTIONS: (1) shouldStopPagination flag - In the new structure this flag is set and checked within the same outer iteration. Direct break calls at each assignment site would be equally clear with less cognitive overhead. Style only, no correctness impact. (2) pageData typed as any[] | null - Pre-existing pattern throughout the file, discards Supabase SDK type info. No action required in this PR but worth addressing in a future cleanup pass. |
|
THEANSWER-SPECIFIC CHECKLIST: tags:[AAI] present on AAIDomains_DocumentLoaders (line 30) confirmed. Document loader component only - no routes/controllers/services modified so multi-tenancy and enforceAbility checks are not applicable. No new database queries introduced. No credentials or secrets touched. No unit or integration tests added (see Test Coverage note). |
|
TEST COVERAGE: No tests added or updated. Given that the bug (retry loop re-invoking onPage on transient DB failures) is non-trivial to reproduce manually, a focused unit test for _executeLoad would significantly increase confidence and guard against regressions. Suggested approach: mock the Supabase client to return a successful page while the onPage callback throws on the first call, then verify that onPage is called exactly once per page and the error propagates without triggering a re-fetch of the same page. |
|
POSITIVE OBSERVATIONS: The refactor correctly separates concerns at the right boundary: network-level retries vs. application-level callback processing - this is the right architectural decision. Commit message follows conventional commit format with a thorough body explaining both root cause and fix. Renaming error to fetchError in the catch block makes the error origin unambiguous at a glance. The inline comment at line 573 clearly documents the intended error-propagation behaviour for future maintainers. Cursor advancement (lastId) is only updated after onPage succeeds, correctly preserved from the original. PR targets staging as required by project conventions. |
|
NEXT STEPS: (1) Consider adding a unit test for _executeLoad covering the separated retry/callback boundary to protect this fix from future regressions. (2) Optionally remove or annotate the dead-code guard at line 567. (3) The logic of the fix is sound. The PR is otherwise ready to merge to staging. |
chunkRepository.find() was loading full DocumentStoreFileChunk entities
including pageContent (up to 1KB each). With 95k existing chunks from a
previous sync, this was loading ~143MB into memory before streaming even
started — explaining the crash at page 20 despite streaming being live.
Add select: { id: true } to both existingChunkIds fetches in
_saveChunksToStorage and syncAndRefreshChunks.
PR Review: fix(AAIDomains): separate Supabase fetch retry from onPage callbackSummary: This PR fixes a genuine production bug where a failed DB save inside Critical IssuesNone. Major ConcernsNone. The core bug fix is correct. Minor Issues and Suggestions1. Dead code guard on line 567 of Location: The inner
There is no code path through which the inner loop exits normally with 2. Location: In the original code, 3. Silent swallow of Location: This is pre-existing behavior and not introduced by this PR, but the PR description explicitly calls out that Positive Observations
TheAnswer-Specific Checklist
Next StepsThe fix is safe to merge. The suggestions above are all minor; none block the PR. If bandwidth allows, adding a test for the |
Problem
In
_executeLoad, the retry loop was catching ALL errors — including errors from theonPagecallback (DB save). A failed DB insert would cause_executeLoadto re-fetch the same Supabase page and retry the save, potentially creating duplicate chunks in the document store.Fix
Restructure
_executeLoadso the retry loop covers only the Supabase fetch.onPageis called after a successful fetch, outside the retry block. IfonPagethrows, the error propagates up to the caller (_saveChunksToStoragecatches it and setssaveFailed = true).Also fixes: Production server is running 3.0.11 (PR #1024 image) without
loadStream—typeof docNodeInstance.loadStream === 'function'returns false, falls back toinit()which loads all pages. This PR + staging deploy will activate the streaming path.