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

Dexie.Observable: startObserving function: remove read-only query in order to avoid TransactionInactiveError #1138

Merged
merged 11 commits into from Oct 14, 2020

Conversation

jayaddison
Copy link
Contributor

@jayaddison jayaddison commented Oct 6, 2020

The cause of #1126 seems to relate to conflict between the (re)use of transactions on the _syncNodes Dexie-internal table after the ready trigger fires but before the database has completed opening.

The attempted fix here replaces two separate read & then write operations with a single-pass modify operation over the db._syncNodes internal table -- relying on Table.orderBy to ensure that existing master sync node state is assessed before the 'local' sync node record is updated -- within a Dexie.ignoreTransaction -- to retain atomicity, while avoiding re-use of the 'current' transaction.

Resolves #1126

@jayaddison
Copy link
Contributor Author

NB: This does pass existing tests (locally here, at least), but additional test coverage would be worthwhile to prove the fix and prevent regressions (assuming it's a reasonable/correct approach, which it may not be!)

@jayaddison jayaddison changed the title Dexie.Observable: Do not re-use current transaction during startObserving function; avoids TransactionInactiveError Dexie.Observable: startObserving function: resolve TransactionInactiveError Oct 7, 2020
@jayaddison jayaddison changed the title Dexie.Observable: startObserving function: resolve TransactionInactiveError Dexie.Observable: startObserving function: fix TransactionInactiveError issue Oct 7, 2020
pollHandle = setTimeout(poll, LOCAL_POLL);
// Start heartbeat
heartbeatHandle = setTimeout(heartbeat, HEARTBEAT_INTERVAL);
db.transaction('rw!', '_syncNodes', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 'works', but needs a bit of scrutiny as to whether it retains atomicity:

Ideally some test coverage to check & prove atomicity would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another related thought: this is currently essentially performing a get-and-set within a transaction to ensure atomicity.

Would it be possible instead to use a single write operation (Table.modify or similar) to perform the required updates?

That way there would be no read-only query required within the transaction, and the transaction would not risk becoming inactive as a result of that query completing before any writes occur.

Our requirements are to:

  • Set the current node's isMaster flag when no record with isMaster and an up-to-date heartbeat timestamp is present
  • Unset the isMaster flag on a record if the heartbeat timestamp is out-of-date
  • Write the current node record into the table

The challenge is that the value we write for the current node record depends on the contents of a potentially existing (or potentially nonexistent) record with isMaster set and potentially out-of-date heartbeat timestamp.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does look like the isMaster field is an indexable type (ref: definition as Number), so perhaps it would be possible to use db._syncNodes.orderBy(...).modify(...) to retrieve existing syncnodes in order, maintain an external variable to store the 'current node should become master' state, and then use a function argument to modify to write the expected object record(s).

@jayaddison jayaddison changed the title Dexie.Observable: startObserving function: fix TransactionInactiveError issue Dexie.Observable: startObserving function: remove read-only query in order to avoid TransactionInactiveError Oct 8, 2020
@jayaddison
Copy link
Contributor Author

@dfahlander Apologies for any notification noise from this and the associated issue over the past few days; it's taken a while to work out what's been going on, and attempt different approaches to a solution :)

I think this is ready for another look now; I'm becoming more confident in the correctness of this after understanding the purpose of the code a little better, and given the test coverage in Dexie itself and also when using it alongside the dexie-1126-repro example case.

Copy link
Collaborator

@dfahlander dfahlander left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the deep dive in this issue and for finding a solution.

@dfahlander dfahlander merged commit 1f2cad1 into dexie:master Oct 14, 2020
@jayaddison jayaddison deleted the fixes/issue-1126 branch October 14, 2020 09:14
@jayaddison
Copy link
Contributor Author

No problem, thanks!

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

Successfully merging this pull request may close these issues.

OpenFailedError: TransactionInactiveError when using indexeddbshim + dexie
2 participants