Skip to content

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

Merged
merged 5 commits into from
Nov 22, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@

import static com.google.firebase.firestore.util.Assert.hardAssert;

import android.database.sqlite.SQLiteTransactionListener;
import android.util.SparseArray;
import com.google.firebase.firestore.core.ListenSequence;
import com.google.firebase.firestore.model.DocumentKey;
import com.google.firebase.firestore.model.ResourcePath;
import com.google.firebase.firestore.util.Consumer;

/** Provides LRU functionality for SQLite persistence. */
class SQLiteLruReferenceDelegate implements ReferenceDelegate, LruDelegate {
class SQLiteLruReferenceDelegate
implements ReferenceDelegate, LruDelegate, SQLiteTransactionListener {
private final SQLitePersistence persistence;
private ListenSequence listenSequence;
private long currentSequenceNumber;
Expand All @@ -40,6 +42,19 @@ void start(long highestSequenceNumber) {
listenSequence = new ListenSequence(highestSequenceNumber);
}

@Override
public void onBegin() {
onTransactionStarted();
}

@Override
public void onCommit() {
onTransactionCommitted();
}

@Override
public void onRollback() {}

@Override
public void onTransactionStarted() {
hardAssert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,35 +143,32 @@ RemoteDocumentCache getRemoteDocumentCache() {

@Override
void runTransaction(String action, Runnable operation) {
Logger.debug(TAG, "Starting transaction: %s", action);
db.beginTransactionWithListener(referenceDelegate);
Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, though by moving it in here, it clutters the transaction logic, making it more difficult to audit. See 496e614

However, we can create an adapter to (a) reduce the duplication and (b) move the clutter out from the transaction methods. See 2931f24

wdyt?

Copy link
Contributor

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);

Copy link
Contributor

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. :-)

Copy link
Member Author

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.

try {
Logger.debug(TAG, "Starting transaction: %s", action);
referenceDelegate.onTransactionStarted();
db.beginTransaction();
operation.run();

// Note that an exception in operation.run() will prevent this code from running.
db.setTransactionSuccessful();
} finally {
db.endTransaction();
referenceDelegate.onTransactionCommitted();
}
}

@Override
<T> T runTransaction(String action, Supplier<T> operation) {
Logger.debug(TAG, "Starting transaction: %s", action);
T value = null;
db.beginTransactionWithListener(referenceDelegate);
try {
Logger.debug(TAG, "Starting transaction: %s", action);
referenceDelegate.onTransactionStarted();
db.beginTransaction();
T value = operation.get();
value = operation.get();

// Note that an exception in operation.run() will prevent this code from running.
db.setTransactionSuccessful();
return value;
} finally {
db.endTransaction();
referenceDelegate.onTransactionCommitted();
}
return value;
}

/**
Expand Down