fix(search): bulkPut hangs instead of rejecting when an IndexedDB write fails - #637
fix(search): bulkPut hangs instead of rejecting when an IndexedDB write fails#637ppcvote wants to merge 4 commits into
Conversation
TableWrapper.bulkPut ran its work inside an async Promise executor with no reject, so a rejected chunk write settled the executor's own promise rather than the returned one. The promise never settled at all. SearchService's cold-cache path waits on it behind `while (!searchServiceIsLoaded)`, so the parsing spinner stays up for good, and the .catch already written for that path in index.js could not run because nothing ever rejected. Signed-off-by: ppcvote <risky9763@gmail.com>
Signed-off-by: ppcvote <risky9763@gmail.com>
|
Thanks! The underlying If you are able to tackle that, great! If not, then I can get to work on it eventually. I appreciate the contribution though! |
A failed cold start left searchServiceIsLoaded false, and `search` waits on that flag in a loop, so every query after the failure re-showed the parsing icon every 100ms for as long as the page stayed open. The warm restore path had the opposite problem: its `finally` set the flag to true unconditionally, overriding the false its own `catch` had just set, so a failed restore reported success and then queried a SearchService that never initialized. Both paths now put the controls into the same unavailable state the unsupported browser branch already used, which that branch now shares, and `search` stops waiting once the index is known not to be coming. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two tests added with the fix both drive the cold-start branch. This adds one for the cached branch, where the `finally` used to override the `catch`, so both halves of the change have a test that fails without it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Took it. Pushed 062f477 and 0d58ced. You were right about the cold path. The The warm restore path had the opposite problem. Its Both paths now call one Three tests in |
Description of what has changed
TableWrapper.bulkPutran its work insidenew Promise(async (resolve) => ...)with noreject, and the chunk write atindexed-db-wrapper.js:60was unguarded. When Dexie rejects there (QuotaExceededError, DatabaseClosedError, an aborted transaction) the rejection settled the executor's own throwaway promise, so the promise returned to the caller never settled at all. It hung rather than rejected.That matters because
SearchService.initializeAsyncawaits it on the cold-cache path, andindex.js:162then spins onwhile (!searchServiceIsLoaded)with the parsing spinner shown. The.catchalready written for that path atindex.js:139could not run, because nothing ever rejected.The fix takes
rejectand wraps the chunk loop in try/catch, following the shape already used inbackupSearchIndex. Droppingasyncfrom the executor also clearsno-async-promise-executor, which this repo's eslint config sets to error: one problem at line 26 before, none after.Two tests added to the existing file. They race the call against a 1s sentinel so a hang is distinguishable from a rejection; a plain
rejectsassertion would just time out and read as a slow test. Both reportReceived: "HUNG"before the change and pass after. Full suite: 44 passing.CHANGELOG.mdhas anUnreleasedsection for this. There was no pending heading, so please fold it into whichever version you cut next.Issues addressed by pull request
None open that I could find; the failure is silent, so it would surface as "search never finishes loading" rather than an error.
@isaisabel for review, per the template.