Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReadonlyError: Write transactions not allowed within liveQueries #1760

Closed
dfahlander opened this issue Jul 6, 2023 · 1 comment
Closed
Labels

Comments

@dfahlander
Copy link
Collaborator

The error "ReadonlyError: Write transactions not allowed within liveQueries" can happen if a liveQuery() callback performs write operations instead of pure readonly operations. The error does only occur in dexie@4.0.1-alpha.11 and later and is a way to help application developers to be aware that live queries must be pure readonly and never mutate data.

However, there has been a bug that could make this error be triggered even when no liveQuery callback does any mutable operation.

Background:

An issue was filed via direct interaction with a dexie-cloud customer: Customer sees failures in console log: Error type: "ReadOnlyError". Error message: "Write transactions not allowed within liveQueries". The customer code did not perform anything else than dexie queries in their callbacks to useLiveQuery(). It turned out that Dexie's async context could leak to unrelated code. Reason for the context leakage was a combination of a generic bug in the async context that was only hit if the async context was created without also encapsulating it in a new microTask scope. Transactions scopes do not leak because db.transaction always makes sure to fire the transaction callback within a controlled microTask scope. liveQuery uses asyncContext to track visited queries but was hit by the bug because when the first dexie-returned promise was requested, the generic engine in promsie.js detected that it was not within a microtick, so it enqueued a new microtick using Promise.resolve().then() that derived the current context. When additional promises was enqueued before that microtick engine was started, they did execute within the liveQUery context, but the correct thing would be that the microtick engine always started from the root context no matter who happened to enqueue the microtask flow.

The issue is fixed in dexie@4.0.1-alpha.24, and could be summarized as follows:

  1. When microTask engine starts, initialize the async context to the root context before executing the tasks. This is also needed because Dexie's zone echoing makes both native await and queueMicroTask() derive the async context.
  2. Optimization: use queueMicroTask() instead of Promise.resolve().then() to enqueu the microtask flow (this way not deriving async contexts by default and this is also faster)
  3. Optimization II: Execute the microtask engine directly within the liveQuery's execute() function so it never have to enqueue it for later use.
@dfahlander
Copy link
Collaborator Author

dfahlander commented Jul 6, 2023

Fixed in dexie@4.0.1-alpha.24 (commit 988d73f)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant