Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -29,6 +29,7 @@
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.firestore.FirebaseFirestoreException.Code;
import com.google.firebase.firestore.core.TransactionRunner;
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
import com.google.firebase.firestore.util.AsyncQueue.TimerId;
import java.util.ArrayList;
Expand Down Expand Up @@ -632,6 +633,27 @@ public void testDoesNotRetryOnPermanentError() {
assertEquals(1, count.get());
}

@Test
public void testMakesDefaultMaxAttempts() {
FirebaseFirestore firestore = testFirestore();
DocumentReference doc1 = firestore.collection("counters").document();
AtomicInteger count = new AtomicInteger(0);
waitFor(doc1.set(map("count", 15)));
Task<Void> transactionTask =
firestore.runTransaction(
transaction -> {
// Get the first doc.
transaction.get(doc1);
// Do a write outside of the transaction to cause the transaction to fail.
waitFor(doc1.set(map("count", 1234 + count.incrementAndGet())));
return null;
});

Exception e = waitForException(transactionTask);
assertEquals(Code.FAILED_PRECONDITION, ((FirebaseFirestoreException) e).getCode());
assertEquals(TransactionRunner.DEFAULT_MAX_ATTEMPTS_COUNT, count.get());
}

@Test
public void testSuccessWithNoTransactionOperations() {
FirebaseFirestore firestore = testFirestore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@

/** TransactionRunner encapsulates the logic needed to run and retry transactions with backoff. */
public class TransactionRunner<TResult> {
private static final int RETRY_COUNT = 5;
public static final int DEFAULT_MAX_ATTEMPTS_COUNT = 5;
private AsyncQueue asyncQueue;
private RemoteStore remoteStore;
private Function<Transaction, Task<TResult>> updateFunction;
private int retriesLeft;
private int attemptsRemaining;

private ExponentialBackoff backoff;
private TaskCompletionSource<TResult> taskSource = new TaskCompletionSource<>();
Expand All @@ -44,7 +44,7 @@ public TransactionRunner(
this.asyncQueue = asyncQueue;
this.remoteStore = remoteStore;
this.updateFunction = updateFunction;
this.retriesLeft = RETRY_COUNT;
this.attemptsRemaining = DEFAULT_MAX_ATTEMPTS_COUNT;

backoff = new ExponentialBackoff(asyncQueue, TimerId.RETRY_TRANSACTION);
}
Expand All @@ -56,6 +56,7 @@ public Task<TResult> run() {
}

private void runWithBackoff() {
attemptsRemaining -= 1;
backoff.backoffAndRun(
() -> {
final Transaction transaction = remoteStore.createTransaction();
Expand Down Expand Up @@ -84,8 +85,7 @@ private void runWithBackoff() {
}

private void handleTransactionError(Task task) {
if (retriesLeft > 0 && isRetryableTransactionError(task.getException())) {
retriesLeft -= 1;
if (attemptsRemaining > 0 && isRetryableTransactionError(task.getException())) {
runWithBackoff();
} else {
taskSource.setException(task.getException());
Expand Down