You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Traditionally, our DB layer has supported automatically nested DB transactions. If you called Zotero.DB.beginTransaction() twice, you ended up in transaction level 1 (up from 0), and commitTransaction() had to be called twice before the changes were actually committed. This allowed data layer functions to wrap SQL statements in transactions and be sure that the data would remain consistent, regardless of whether the function was called directly or called by another function that had already opened a transaction itself.
With async DB access, I don't think we can safely support nested transactions. Since statements within async transactions yield, a second, unrelated executeTransaction() call could come in in the middle of a transaction. At best, this would mean that the unrelated transaction would automatically nest, such that a failure of one would affect the other. At worst, this could result in statements from the second transaction being run outside a transaction, since the first transaction could complete first and be closed in Sqlite.jsm.
My solution is to remove support for nested transactions and have all transactions block (up to a timeout) on other transactions. Unfortunately, this means that functions can't open transactions if there's any chance that they could be called (at any point up the stack) by other functions that open transactions, since the called function will block on the calling function. And since those called functions still require data consistency, they need to fail if called outside a transaction (Zotero.DB.requireTransaction()).
Having to know the transaction requirements of functions from above is definitely a pain, but I don't have a better solution. In most cases, at least, I think it will be clear where the transaction should go.
Zotero.DataObject.prototype.save() is a special case, since it's called so frequently on its own (e.g., in various UI code, all throughout tests, in third-party code), so I'm creating a saveTx() function that tells save() to start a transaction itself. Any code that just creates an individual objects can use that. I think I'm also going to have save() start a transaction automatically (which in most cases won't be a problem) if one isn't open but log a warning advising the use of saveTx(), just so that everything doesn't break while we're fixing old calls.
The text was updated successfully, but these errors were encountered:
Traditionally, our DB layer has supported automatically nested DB transactions. If you called
Zotero.DB.beginTransaction()
twice, you ended up in transaction level 1 (up from 0), andcommitTransaction()
had to be called twice before the changes were actually committed. This allowed data layer functions to wrap SQL statements in transactions and be sure that the data would remain consistent, regardless of whether the function was called directly or called by another function that had already opened a transaction itself.With async DB access, I don't think we can safely support nested transactions. Since statements within async transactions yield, a second, unrelated
executeTransaction()
call could come in in the middle of a transaction. At best, this would mean that the unrelated transaction would automatically nest, such that a failure of one would affect the other. At worst, this could result in statements from the second transaction being run outside a transaction, since the first transaction could complete first and be closed in Sqlite.jsm.My solution is to remove support for nested transactions and have all transactions block (up to a timeout) on other transactions. Unfortunately, this means that functions can't open transactions if there's any chance that they could be called (at any point up the stack) by other functions that open transactions, since the called function will block on the calling function. And since those called functions still require data consistency, they need to fail if called outside a transaction (
Zotero.DB.requireTransaction()
).Having to know the transaction requirements of functions from above is definitely a pain, but I don't have a better solution. In most cases, at least, I think it will be clear where the transaction should go.
Zotero.DataObject.prototype.save()
is a special case, since it's called so frequently on its own (e.g., in various UI code, all throughout tests, in third-party code), so I'm creating asaveTx()
function that tellssave()
to start a transaction itself. Any code that just creates an individual objects can use that. I think I'm also going to havesave()
start a transaction automatically (which in most cases won't be a problem) if one isn't open but log a warning advising the use ofsaveTx()
, just so that everything doesn't break while we're fixing old calls.The text was updated successfully, but these errors were encountered: