simolus3 / drift

Drift is an easy to use, reactive, typesafe persistence library for Dart & Flutter.
https://drift.simonbinder.eu/
MIT License
2.45k stars 354 forks source link

No modification allowed Error #3063

Open Surio89 opened 1 week ago

Surio89 commented 1 week ago

In my Flutter Web export, I somehow ended up in a state that no longer allows me to access the database. Unfortunately I don't know how to reproduce this.

I get the following error message:

`NoModificationAllowedError: No modification allowed main.dart.js:63922:17 ===== asynchronous gap =========================== main.dart.js:63922:17 main.dart.js 16597:38 StackTrace_current main.dart.js:63922:17 main.dart.js 111079:131 request$1$2$requestId main.dart.js:63922:17 main.dart.js 111084:19 request$1$1 main.dart.js:63922:17 main.dart.js 111002:102 ensureOpen$body$_RemoteQueryExecutor. main.dart.js:63922:17 main.dart.js 13632:17 _wrapJsFunctionForAsync. main.dart.js:63922:17 main.dart.js 94958:12 call$2 main.dart.js:63922:17 main.dart.js 13596:20 _asyncStartSync main.dart.js:63922:17 main.dart.js 111011:16 ensureOpen$body$_RemoteQueryExecutor main.dart.js:63922:17 main.dart.js 110986:19 ensureOpen$1 main.dart.js:63922:17 main.dart.js 114516:17 call$1 main.dart.js:63922:17 main.dart.js 14212:18 _rootRunUnary main.dart.js:63922:17 main.dart.js 247136:16 main.dart.js:63922:17 main.dart.js 97440:39 runUnary$2$2 main.dart.js:63922:17 main.dart.js 95952:51 call$0 main.dart.js:63922:17 main.dart.js 13949:93 _Future__propagateToListeners main.dart.js:63922:17 main.dart.js 95816:9 _completeWithValue$1 main.dart.js:63922:17 main.dart.js 95894:18 call$0 main.dart.js:63922:17 main.dart.js 14202:16 _rootRun main.dart.js:63922:17 main.dart.js 247132:16 main.dart.js:63922:17 main.dart.js 97435:39 run$1$1 main.dart.js:63922:17 main.dart.js 97371:14 runGuarded$1 main.dart.js:63922:17 main.dart.js 97565:25 call$0 main.dart.js:63922:17 main.dart.js 14010:24 _microtaskLoop main.dart.js:63922:17 main.dart.js 14016:11 _startMicrotaskLoop main.dart.js:63922:17 main.dart.js 94837:9 call$1 main.dart.js:63922:17 main.dart.js 13559:9 MutationCallback*_AsyncRun__initializeScheduleImmediate main.dart.js:63922:17 main.dart.js 256829:110 main.dart.js:63922:17 main.dart.js 116:21 lazyFinal. main.dart.js:63922:17 main.dart.js 14030:13 _scheduleAsyncCallback main.dart.js:63922:17 main.dart.js 14255:9 _rootScheduleMicrotask main.dart.js:63922:17 main.dart.js 97734:9 scheduleMicrotask$1 main.dart.js:63922:17 main.dart.js 95832:18 _asyncCompleteWithValue$1 main.dart.js:63922:17 main.dart.js 95828:12 _asyncComplete$1 main.dart.js:63922:17 main.dart.js 94923:23 complete$1 main.dart.js:63922:17 main.dart.js 13604:17 _asyncReturn main.dart.js:63922:17 main.dart.js 20137:24 bootstrapEngine. main.dart.js:63922:17 main.dart.js 13632:17 _wrapJsFunctionForAsync. main.dart.js:63922:17 main.dart.js 94958:12 call$2 main.dart.js:63922:17 main.dart.js 13596:20 _asyncStartSync main.dart.js:63922:17 main.dart.js 20140:16 bootstrapEngine main.dart.js:63922:17 main.dart.js 63900:38 main. main.dart.js:63922:17 main.dart.js 13632:17 _wrapJsFunctionForAsync. main.dart.js:63922:17 main.dart.js 94958:12 call$2 main.dart.js:63922:17 main.dart.js 13596:20 _asyncStartSync main.dart.js:63922:17 main.dart.js 63907:16 main main.dart.js:63922:17 main.dart.js 257504:15 main.dart.js:63922:17 main.dart.js 257485:15 main.dart.js:63922:17 main.dart.js 257498:5 dartProgram main.dart.js:63922:17 main.dart.js 257507:3 main.dart.js:63922:17

2 main.dart.js:63922:17` This error is currently occurring in Firefox and I see that WasmStorageImplementation.opfsShared is being used. Does anyone have any idea what this could be and how I can at least delete the database from Firefox so that it is recreated? Other browsers work fine.
simolus3 commented 1 week ago

No idea on what could have caused this yet. Docs say that NoModificationAllowedError may be thrown by the browser then accessing the same OPFS file multiple times. Can you check whether there's anything in your app that may cause two different database instances to be around without first closing the first one?

If opening the same database two times at the same time is indeed the problem here, deleting databases won't fix this. One way to delete all files for a website is to go to about:settings, then "Privacy & Security", then "Manage data..." under "Cookies and Website Data". You can entirely clear all content Firefox has set for a given domain there. Deleting OPFS files is tricky since they're not listed in Firefox Devtools, but I think pasting this snippet to the console should work if no database instance is currently open:

(async () => {
  let opfsRoot = await navigator.storage.getDirectory();
  let deleted = 0;
  for await (const [key, value] of opfsRoot.entries()) {
    await opfsRoot.removeEntry(key, { recursive: true });
    deleted++;
  }

  console.log(`Done, deleted ${deleted} entries!`);
})()
Surio89 commented 1 week ago

Thanks for the quick answer!

If i run the script in Firefox i receive: `Promise { : "pending" } ​: "rejected"

: DOMException: No modification allowed code: 7 columnNumber: 0 data: null filename: "" lineNumber: 0 message: "No modification allowed" name: "NoModificationAllowedError" result: 2152923143 stack: "" : DOMExceptionPrototype { name: Getter, message: Getter, INDEX_SIZE_ERR: 1, … } : Promise.prototype { … }` When i run in in another Browser it deletes the db as expected and after a reload the db is recreated and works as expected. Firefox runs with WasmStorageImplementation.opfsShared and my other Browsers with WasmStorageImplementation.opfsLocks. On another machine the App works fine in Firefox.
simolus3 commented 1 week ago

If the No modification allowed exception happens, your database is currently open somewhere. You could try serving a version of your app once where you never open the database just to check and delete OPFS files, but the problem will likely come back afterwards.

With opfsLocks, each tab uses its own database instance and briefly opens the file only when it needs to read or write it. opsShared is a more efficient mode where we use a shared worker that keeps the database files open all the time and serves requests coming in from all tabs. Since a shared worker is used, it should be impossible to reach that state. To help me debug this, could you look at about:debugging#workers and see if there are multiple shared workers for your application active? You mentioned that you don't have a way to reproduce this, does the problem not go away after closing your app and re-opening it? Or after deleting data for your app in Firefox settings?

Surio89 commented 1 week ago

I managed to delete the db with the script before the app opens the db.

After a reload i noticed, that the app now runs with WasmStorageImplementation.sharedIndexedDb and i receive:

NotFoundError: IDBDatabase.transaction: 'files' is not a known object store name

error.

Im stuck in this state until i delte the indexed db via dev tools and reload.

After reload im in WasmStorageImplementation.opfsShared mode again and everything works as expected!

The app now works fine from the affected browser. unfortunately I don't know how to reproduce the initial problem. When I had the initial problem, neither reloading nor restarting the browser helped.

If i have an open tab with the app the abount:debugging#workers shows this:

image

If i close the tab the three entries disappear.