Skip to content

Firestore Transactions: Keep Retries Separate from External Side Effects

Define conflict handling and external notifications around Firestore transactions so repeated callbacks do not become repeated user actions.

GuideDeveloper tools

By

Updated 2 min read
Firestore Transaction Retries: database illustration with IndieTools branding

A Firestore transaction callback can run more than once for one user operation. Keep external actions such as email delivery or document publication outside that retryable callback, and define how the application recognizes an operation that already completed.

The official transaction guide describes retries when a concurrent edit affects a document read by the transaction. It also states that transaction writes apply together and that client transactions fail offline. These properties should shape the product's save behavior.

Name the invariant first

A transaction is useful when related values must agree. Write that condition in ordinary language before selecting documents or writing code.

For a document editor, the condition might be that a saved revision and its current-version reference advance together. For a reservation system, it might be that capacity never becomes negative.

Do not place every write in a transaction merely because it sounds safer. Identify which decision depends on a value that another client could change.

Separate preparation from commitment

Prepare a validated request, then let the transaction read the necessary current state and decide whether it can commit. Keep its callback focused on that decision and its related database changes.

MakeResume describes an editing and export workflow and appears in the Firestore collection. Its internal save implementation is not documented in the listing. A resume-style editor illustrates the distinction: requesting an export, saving a revision and sending an export notification are different operations.

A callback retry must not silently send the notification again.

Give the user one operation reference

Create a stable reference for an intentional operation and preserve it when the interface retries after a timeout. A fresh reference on every network attempt makes reconciliation harder.

Store enough durable information to determine whether the operation committed. If the response disappears after commitment, the next request should be able to discover the result instead of blindly repeating the action.

External delivery still needs its own duplicate protection. A database transaction does not make an email provider participate in the same commit.

Define a conflict outcome

Some changes can be recomputed against the latest state. Others need the user to resolve competing edits. Decide which category the operation belongs to.

For a document revision, show that a newer version exists and preserve the user's unsaved work. Do not replace it with an unexplained generic failure or silently overwrite another editor's changes.

Make retry limits and terminal failures observable to support staff without exposing document contents.

Exercise interruption deliberately

In an isolated test, submit concurrent edits against the same starting revision. Record the final documents and the number of externally delivered effects. Repeat with a lost response after a successful write.

Also test offline behavior. If the interface offers local editing, distinguish locally retained changes from a transaction confirmed by the server. A success label should correspond to the state the user reasonably expects.

The Firestore ownership guide covers the separate permission boundary. Combine those checks with concurrency evidence before introducing collaboration, export automation or background notifications.

Keep these fixtures after launch. Changes to transaction scope or delivery code can reintroduce duplicate work even when ordinary single-user saving still looks correct.

More guide articles