firestore: reset transaction write state in between retries

Change-Id: I308aaaea52bb25a50aeca42bfda65ae54fefdf0c
Reviewed-on: https://code-review.googlesource.com/c/36092
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Michael Lehenbauer <mikelehen@google.com>
Reviewed-by: Eno Compton <enocom@google.com>
diff --git a/firestore/transaction.go b/firestore/transaction.go
index 5bfe3e7..20a8a2a 100644
--- a/firestore/transaction.go
+++ b/firestore/transaction.go
@@ -172,6 +172,9 @@
 			err = cerr
 			break
 		}
+
+		// Reset state for the next attempt.
+		t.writes = nil
 	}
 	// If we run out of retries, return the last error we saw (which should
 	// be the Aborted from Commit, or a context error).
diff --git a/firestore/transaction_test.go b/firestore/transaction_test.go
index 3955a1a..758d11c 100644
--- a/firestore/transaction_test.go
+++ b/firestore/transaction_test.go
@@ -392,3 +392,77 @@
 		t.Fatal(err)
 	}
 }
+
+// Each retry attempt has the same amount of commit writes.
+func TestRunTransaction_Retries(t *testing.T) {
+	ctx := context.Background()
+	const db = "projects/projectID/databases/(default)"
+	tid := []byte{1}
+	c, srv := newMock(t)
+
+	srv.addRPC(
+		&pb.BeginTransactionRequest{Database: db},
+		&pb.BeginTransactionResponse{Transaction: tid},
+	)
+
+	aDoc := &pb.Document{
+		Name:       db + "/documents/C/a",
+		CreateTime: aTimestamp,
+		UpdateTime: aTimestamp2,
+		Fields:     map[string]*pb.Value{"count": intval(1)},
+	}
+	aDoc2 := &pb.Document{
+		Name:   aDoc.Name,
+		Fields: map[string]*pb.Value{"count": intval(7)},
+	}
+
+	srv.addRPC(
+		&pb.CommitRequest{
+			Database:    db,
+			Transaction: tid,
+			Writes: []*pb.Write{{
+				Operation:  &pb.Write_Update{aDoc2},
+				UpdateMask: &pb.DocumentMask{FieldPaths: []string{"count"}},
+				CurrentDocument: &pb.Precondition{
+					ConditionType: &pb.Precondition_Exists{true},
+				},
+			}},
+		},
+		status.Errorf(codes.Aborted, "something failed! please retry me!"),
+	)
+
+	srv.addRPC(
+		&pb.BeginTransactionRequest{
+			Database: db,
+			Options: &pb.TransactionOptions{
+				Mode: &pb.TransactionOptions_ReadWrite_{
+					&pb.TransactionOptions_ReadWrite{RetryTransaction: tid},
+				},
+			},
+		},
+		&pb.BeginTransactionResponse{Transaction: tid},
+	)
+
+	srv.addRPC(
+		&pb.CommitRequest{
+			Database:    db,
+			Transaction: tid,
+			Writes: []*pb.Write{{
+				Operation:  &pb.Write_Update{aDoc2},
+				UpdateMask: &pb.DocumentMask{FieldPaths: []string{"count"}},
+				CurrentDocument: &pb.Precondition{
+					ConditionType: &pb.Precondition_Exists{true},
+				},
+			}},
+		},
+		&pb.CommitResponse{CommitTime: aTimestamp3},
+	)
+
+	err := c.RunTransaction(ctx, func(_ context.Context, tx *Transaction) error {
+		docref := c.Collection("C").Doc("a")
+		return tx.Update(docref, []Update{{Path: "count", Value: 7}})
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+}