-
Notifications
You must be signed in to change notification settings - Fork 615
Fix handling of sqlite transactions #136
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
Conversation
In particular, don't do anything that could throw in the finally block, as this will mask any exception thrown within the transaction itself.
Using sqlite's version allows us to not worry about getting the transaction semantics correct in the face of exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! I didn't know SQLiteTransactionListener existed. Very nice.
I have a slight preference to not have SQLiteLruReferenceDelegate implement SQLiteTransactionListener, but this is okay as-is too.
@@ -143,35 +143,32 @@ RemoteDocumentCache getRemoteDocumentCache() { | |||
|
|||
@Override | |||
void runTransaction(String action, Runnable operation) { | |||
Logger.debug(TAG, "Starting transaction: %s", action); | |||
db.beginTransactionWithListener(referenceDelegate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since ReferenceDelegate has public onTransactionStarted() / onTransactionCommitted() methods as part of its interface contract, I think it might be cleaner to leave SQLiteLruReferenceDelegate alone, and have an anonymous implementation of SQLiteTransactionListener here that calls the existing methods on the referenceDelegate.
I know this will result in some duplication here since we start transactions in two places, but I think it keeps the contract cleaner. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably isn't worth churning on too much, but having an "Adapter" class introduces some conceptual complexity. I'd actually just have the listener be a private class member initialized via an anonymous class:
// Create a SQLiteTransactionListener that delegates to our referenceDelegate.
private final SQLiteTransactionListener transactionListener =
new SQLiteTransactionListener() {
@Override
public void onBegin() {
referenceDelegate.onTransactionStarted();
}
@Override
public void onCommit() {
referenceDelegate.onTransactionCommitted();
}
@Override
public void onRollback() {}
});
...
db.beginTransactionWithListener(transactionListener);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT? I'm fine with you just submitting as-is too. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that makes more sense; done.
And to remove clutter around the sqlite transaction.
/retest |
... instantiated as an anon implementation of the interface.
@rsgowman: The following test failed, say
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks!
Have we confirmed that an exception thrown in one of these callbacks gets properly reported? The documentation on the listener itself is extremely sparse and I'm worried that while this is definitely conceptually cleaner than what we've done before the lack of direct control over sequencing may not actually help uncover the root cause in issues like #115. |
"sparse" is a nicer way to put it than I'd use. :) I looked at the source instead (incl for those older versions of android) to make sure it's doing reasonable things in the presence of exceptions. Summary: For the onBegin() callback, sqlite: For the onCommit/onRollback, sqlite: You can loose the original root cause from the notification callbacks if sqlite suffers a serious issue such as failing to execute the 'rollback' statement. (Perhaps a disk error or similar could cause that.) Feel free to take a look yourself. For instance, here's the lollipop version: https://android.googlesource.com/platform/frameworks/base/+/lollipop-release/core/java/android/database/sqlite/SQLiteSession.java (just search for onBegin/onCommit) |
Move as much work as possible out of the sqlite txn.
In particular, don't do anything that could throw in the finally block, as this will mask any exception thrown within the transaction itself.
Use sqlite transaction listeners (rather than doing so manually)
Using sqlite's version allows us to not worry about getting the transaction semantics correct in the face of exceptions.