Transactions and Batch Writes

To handle multiple operations (reads and writes) atomically and efficiently, you can use transaction and batch writes operations to group together operation and run as a single transaction. Oracle Backend with Firebase APIs provides robust support for atomic multi-document operations through transactions and batch writes. Document operations such as grouping multiple document operations into a single atomic unit ensure consistency and rollback capabilities across complex workflows. These mechanisms are essential for maintaining data consistency, especially in applications that require coordinated updates across multiple documents or collections.

This section outlines the conceptual model, use cases, operational behavior, and field-level capabilities of transactions and batch writes. SDK-specific implementations are covered in Transaction and Batch Writes Examples.

Overview

Transaction and batch write mechanisms ensure atomicity, consistency, and isolation, making them ideal for collaborative apps, real-time updates, and multi-step workflows.

Transaction

A transaction is a read-modify-write operation that executes atomically. If any step fails or the underlying data changes during execution, the entire transaction is rolled back and retried.

Transactions are automatically retried if document versions change. Developers should ensure logic is idempotent and bounded.

Transactions are particularly useful when you need to read from database to check the state of a document before performing updates.

Batch Write

A batch write is a way to group multiple write operations into a single request. A batch write operation is a set of independent write operations (create, update, delete) that execute together. All operations are applied in parallel, and they either all succeed or all fail. Unlike transactions, batch writes do not support reading documents. No reads are allowed in batch writes.

Note: In batch writes, if any operation is invalid or unauthorized, the entire batch is rejected. Exercise prudence to validate inputs before execution.

Limitations

Transaction and batch writes have the following limitations:

Prerequisites

Transaction Support

Transactions allow developers to perform multiple read and write operations atomically.

Transactions have the following execution flow:

  1. Begin Transaction: The client initiates a transaction block.

  2. Read Phase: All required documents are read to fetch the current state of one or more documents.

  3. Apply Conditional Logic: Decide whether to proceed with updates based on the read values.

  4. Write Phase: Updates are staged based on read results. Perform setDoc, updateDoc, or deleteDoc operations.

  5. Commit: If no conflicting changes occurred, the transaction is committed. Otherwise, it is rolled back.

If any document read during the transaction changes before commit, Oracle Backend with Firebase APIs automatically rolls back the transaction. This ensures consistency but requires idempotent logic.

A few examples use cases of transactions are:

Transactions are initiated and managed using three key fields in the request payload:

The following must be noted about the transaction support:

Example 18-1 Transaction Request

Suppose that you need to increment the views counter of a recipe only if the current value is less than 1000. Additionally, add a new tag to the recipe if the condition is met.

{
  "transaction": {
    "reads": [
      {
        "path": "/users/Scott/recipes/Cookies",
        "fields": ["views"]
      }
    ],
    "condition": {
      "field": "views",
      "op": "<",
      "value": 1000
    },
    "writes": [
      {
        "type": "updateDoc",
        "path": "/users/Scott/recipes/Cookies",
        "data": {
          "views": { "increment": 1 },
          "tags": { "arrayUnion": ["popular"] }
        }
      }
    ]
  }
}

The following is the outcome:

When to Use Transaction

Batch Writes

Batch writes allow developers to send multiple operations in a single array payload. Unlike transactions, batch writes do not support reads or conditional logic. Each operation is executed in sequence, and the entire batch is treated as a single transaction if begin_trans and end_trans are specified.

The batch writes structure is as follows:

The following is a general workflow:

Example 18-2 Batch Writes

Suppose that you need to create a new recipe document, update the user’s profile with a new counter, and delete an obsolete recipe. The batch request is as follows:

{
  "batch": {
    "operations": [
      {
        "type": "setDoc",
        "path": "/users/Scott/recipes/Cookies",
        "data": {
          "title": "Chocolate Chip Cookies",
          "ingredients": "2 cups flour,1 cup sugar,2 cups chocolate chips",
          "createdAt": "serverTimestamp"
        }
      },
      {
        "type": "updateDoc",
        "path": "/users/Scott",
        "data": {
          "recipeCount": { "increment": 1 }
        }
      },
      {
        "type": "deleteDoc",
        "path": "/users/Scott/recipes/OldRecipe"
      }
    ]
  }
}

This results in the following outcome:

When to Use Batch Writes

A few example use cases of batch write are:

Security and Validation

Field-Level Modifiers

Oracle Backend with Firebase APIs supports powerful field-level operations that enhance both transactions and batch writes. These modifiers are applied within the document payload.

Modifier Description Example
serverTimestamp Stores the current server time. createdAt: serverTimestamp()
increment(n) Atomically adds/subtracts a numeric value. views: increment(1)
arrayUnion(values) Adds values to an array if not already present. tags: arrayUnion("dessert")
arrayRemove(values) Removes matching values from an array. tags: arrayRemove("archived")
deleteField Deletes a field from the document. notes: deleteField()

Example 18-3 Payload

These modifiers are especially useful in real-time apps where multiple clients may update the same document concurrently.

{
  "title": "Chocolate Chip Cookies",
  "tags": arrayUnion("dessert"),
  "createdAt": serverTimestamp(),
  "views": increment(1),
  "notes": deleteField()
}