RESOLVED FIXED324094
Ending a Worker with a readwrite and a readonly IndexedDB transaction in flight leaves that database permanently unable to run any transaction
https://bugs.webkit.org/show_bug.cgi?id=324094
Summary Ending a Worker with a readwrite and a readonly IndexedDB transaction in flig...
Ronan
Reported 2026-09-13 01:40:43 PDT
If a dedicated worker ends while it has BOTH a readwrite and a readonly transaction in flight on the same IndexedDB database, that database can be left permanently unusable. Afterwards, from every context in the origin: - indexedDB.open() on it still SUCCEEDS, and the resulting IDBDatabase reports its object stores correctly; - every transaction taken on that connection then never settles: no complete, no abort, no error, indefinitely; - this affects readonly transactions exactly as much as readwrite ones, on any object store, so it is not a writer queued behind a lock; - an unrelated database in the same origin is completely healthy; - a page reload does not clear it; - opening the origin in a new tab does not clear it; - indexedDB.deleteDatabase() on it reports onblocked and never completes, so a web application cannot even reset its own storage. The effect is a permanent, silent loss of one database for the rest of the browsing session, with no error surfaced to script at any point. The only recovery available to an application is to abandon the database name and rebuild into a new one. Steps to reproduce 1. Serve the attached index.html over HTTP (IndexedDB needs a real origin): python3 -m http.server 8000 2. Open http://localhost:8000/ and press Run. Keep the tab in the foreground. 3. Each iteration: a worker opens a fresh database with two object stores, runs a loop of one-put readwrite transactions on one store and a loop of get readonly transactions on the other, and is terminated by the page at a random point 5-65 ms in. The page then opens that database and asks for one readonly transaction. 4. A WEDGED line is a database on which that transaction never settled within 4 s. Each wedged line then re-checks: an unrelated database, deleteDatabase, a reload, and a new tab. Actual results A few percent of iterations wedge the database permanently. - iPhone 12, iOS 18.3.2, Safari 18.3.1: 12 wedged in 200 (6%) - WebKit r2359 on Linux via Playwright 1.62.1, user agent Version/26.5 Safari/605.1.15: 7 to 13 in 200 across runs Expected results Ending the worker should abort its in-flight transactions and release the database, as it does when only one transaction is in flight. No later transaction on that database should hang. Other engines The same page, unchanged: Chromium 141 via Playwright, 0 wedged in 200. Firefox 145 via Playwright, 0 wedged in 200. What is and is not required Narrowed by varying one thing at a time, 200 runs per variant per engine, automated. On WebKit: - one readwrite transaction held open indefinitely, worker terminated mid-transaction: 0/200 - a stream of multi-store readwrite transactions, no concurrent read, killed anywhere: 0/200 - one put per readwrite transaction, no concurrent read: 0/200 - the above plus a second concurrent READWRITE loop on another store: 0/200 - the above plus a concurrent READONLY loop on another store: 3/200 to 8/200 - same, but the worker ends via self.close() instead of terminate(): 9/200 - same, but the concurrent readonly belongs to the PAGE, a context that survives: 0/200 - a read and a write each awaited to COMMIT, so the worker never has two transactions in flight: 0/1000 So: - it needs two transactions of the DYING context overlapping when it ends, and one of them being readonly; - it does not need many object stores, indexes, long transactions, or any particular point in a transaction's life; - it is not specific to Worker.terminate(): self.close() reproduces it at the highest rate measured, so it is the worker ENDING rather than how it was ended; - an overlapping readonly in a surviving context does not do it. Related bugs, and why this is not one of them - Bug 288682, "IndexedDB in Worker commits when thread is terminated" (RESOLVED FIXED, 292795@main, March 2025). Same trigger family, opposite failure: that one COMMITS a half-finished transaction on worker termination, this one strands the database so nothing can run at all. The analysis there -- that WebKit does not perform microtasks during worker termination and so concludes no further requests are coming -- is in the same machinery this report is about, which may or may not be a coincidence. - Bug 315804, "A readwrite IDBTransaction never fires oncomplete, onerror, or onabort after the user subscribes to web push in an installed PWA" (RESOLVED FIXED, 315609@main, June 2026). Nearly identical symptom, different trigger, and it recovered on its own after about 30 minutes; this does not recover at all, and reproduces on a build newer than that fix as well as on one much older. - Bug 324058, "IndexedDB transactions never start after cross-site back navigation when a service worker controls the page" (NEW, filed 2026-09-12). Same symptom again -- open() succeeds, the first transaction never runs -- with a third unrelated trigger, suspected there to be a regression from bug 322386. - Bug 251203, "IndexedDB requests occasionally fail with no error, freezing browser" (NEW, open since 2023). Describes this symptom in web workers, WebKit-only, with no trigger identified. This report may be the missing reproduction for it; a comment there points here. That three distinct triggers now produce "open() succeeds and then no transaction ever runs" suggests a common cause in transaction scheduling or connection teardown rather than three separate faults. Notes - This is distinct from bug 197050 and bug 202705, which concern IndexedDB after the network process or the application is SUSPENDED. No suspension is involved here. The attached page detects being backgrounded and marks such a run contaminated precisely so the two are not confused; every number above is from a foreground tab. - Impact. A web application that folds data into IndexedDB inside a worker, and whose store performs reads concurrently with writes -- the ordinary pattern, since a read helper returns when its request succeeds and leaves its transaction to auto-commit -- can permanently lose its local database whenever the worker is evicted or restarted at the wrong moment. Nothing in the page can detect it in advance, recover it, or delete it. - Workaround, for anyone who finds this before it is fixed: never let the worker hold two transactions at once, by awaiting each transaction's oncomplete before starting the next rather than awaiting only its request. That measured 0 in 1000 runs. It is not free -- it costs a commit round trip on every read, which measured x1.37 on a single-request read -- and it buys nothing on the two engines that do not have the defect.
Attachments
Reproduction (12.47 KB, text/html)
2026-09-13 01:42 PDT, Ronan
no flags
Ronan
Comment 1 2026-09-13 01:42:09 PDT
Created attachment 481372 [details] Reproduction
Radar WebKit Bug Importer
Comment 2 2026-09-13 22:40:07 PDT
Ahmad Saleem
Comment 3 2026-09-13 22:41:15 PDT
EWS
Comment 4 2026-09-21 20:35:37 PDT
Committed 321564@main (e8f6a9f2b506): <https://commits.webkit.org/321564@main> Reviewed commits have been landed. Closing PR #73930 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.