or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdindex.mdsas.mdtable-client.mdtable-service-client.mdtable-transaction.mdtypes.md
tile.json

table-transaction.mddocs/

Table Transaction Operations

TableTransaction provides a helper class for building batch operations that can be executed atomically within a single partition.

Capabilities

TableTransaction Class

Helper class to build a list of transaction actions for batch operations.

/**
 * Helper class to build a list of transaction actions for batch operations.
 * All operations in a transaction must target entities in the same partition.
 */
class TableTransaction {
  /** List of actions to perform in a transaction */
  readonly actions: TransactionAction[];

  /**
   * Creates a new transaction builder
   * @param actions - Optional initial list of actions
   */
  constructor(actions?: TransactionAction[]);
}

Transaction Actions

Methods for adding different types of operations to the transaction.

/**
 * Adds a create entity action to the transaction
 * @param entity - The entity to create, must include partitionKey and rowKey
 */
createEntity<T extends object>(entity: TableEntity<T>): void;

/**
 * Adds a delete entity action to the transaction
 * @param partitionKey - The partition key of the entity to delete
 * @param rowKey - The row key of the entity to delete
 */
deleteEntity(partitionKey: string, rowKey: string): void;

/**
 * Adds an update entity action to the transaction
 * @param entity - The entity to update, must include partitionKey, rowKey, and etag
 * @param updateMode - Update mode: "Merge" (partial) or "Replace" (full), defaults to "Merge"
 */
updateEntity<T extends object>(entity: TableEntity<T>, updateMode?: UpdateMode): void;

/**
 * Adds an upsert entity action to the transaction
 * @param entity - The entity to upsert, must include partitionKey and rowKey
 * @param updateMode - Update mode: "Merge" (partial) or "Replace" (full), defaults to "Merge"
 */
upsertEntity<T extends object>(entity: TableEntity<T>, updateMode?: UpdateMode): void;

Transaction Action Types

Tuple types representing the different actions that can be performed in a transaction.

/**
 * Represents the Create or Delete Entity operation to be included in a Transaction request
 */
type CreateDeleteEntityAction = ["create" | "delete", TableEntity];

/**
 * Represents the Update or Upsert Entity operation to be included in a Transaction request
 */
type UpdateEntityAction =
  | ["update" | "upsert", TableEntity]
  | ["update" | "upsert", TableEntity, "Merge" | "Replace"];

/**
 * Represents the union of all the available transactional actions
 */
type TransactionAction = CreateDeleteEntityAction | UpdateEntityAction;

/**
 * The different modes for Update and Upsert methods
 * - Merge: Updates an entity by updating the entity's properties without replacing the existing entity.
 * - Replace: Updates an existing entity by replacing the entire entity.
 */
type UpdateMode = "Merge" | "Replace";

Usage Examples:

import { TableClient, TableTransaction, AzureNamedKeyCredential } from "@azure/data-tables";

const credential = new AzureNamedKeyCredential("accountName", "accountKey");
const tableClient = new TableClient("https://myaccount.table.core.windows.net", "Orders", credential);

// Create a transaction with multiple operations
const transaction = new TableTransaction();

// Add various operations to the transaction
transaction.createEntity({
  partitionKey: "2023-09",
  rowKey: "order1",
  customerId: "customer1",
  total: 99.99,
  status: "pending"
});

transaction.updateEntity({
  partitionKey: "2023-09", 
  rowKey: "order2",
  etag: "existing-etag",
  status: "completed"
}, "Merge");

transaction.upsertEntity({
  partitionKey: "2023-09",
  rowKey: "order3", 
  customerId: "customer2",
  total: 149.99,
  status: "processing"
}, "Replace");

transaction.deleteEntity("2023-09", "order4");

// Submit the entire transaction atomically
const result = await tableClient.submitTransaction(transaction.actions);

console.log(`Transaction completed with ${result.subResponses.length} operations`);
result.subResponses.forEach((response, index) => {
  console.log(`Operation ${index + 1}: Status ${response.status}`);
  if (response.error) {
    console.log(`  Error: ${response.error.message}`);
  }
});

Transaction Response

Response structure returned when submitting a transaction.

interface TableTransactionResponse {
  /** Collection of sub responses */
  subResponses: TableTransactionEntityResponse[];
  /** Main Transaction request status code */
  status: number;
  /** Gets a specific response given a row key */
  getResponseForEntity: (rowKey: string) => TableTransactionEntityResponse | undefined;
}

interface TableTransactionEntityResponse {
  /** Entity's etag */
  etag?: string;
  /** Entity's rowKey */
  rowKey?: string;
  /** Sub-response status */
  status: number;
}

Transaction Constraints and Best Practices

Partition Key Requirements:

  • All operations in a transaction must target entities in the same partition
  • Mixed partition keys will cause the transaction to fail
  • This ensures atomicity within the partition boundary

Transaction Limits:

  • Maximum 100 operations per transaction
  • Each operation counts toward the limit regardless of type
  • Transaction payload size limits apply (typically 4MB)

Error Handling:

  • If any operation fails, the entire transaction is rolled back
  • Check individual subResponse.status codes for operation-specific results
  • Common failure scenarios include entity conflicts, missing entities, and ETag mismatches

Performance Considerations:

  • Transactions are atomic but may have higher latency than individual operations
  • Use transactions when atomicity is required, not just for batching
  • Consider partition distribution when designing entity keys for optimal transaction usage
// Example: Error handling and validation
const transaction = new TableTransaction();

// Validate all entities target the same partition
const targetPartition = "2023-09";
const entities = [
  { partitionKey: targetPartition, rowKey: "1", data: "value1" },
  { partitionKey: targetPartition, rowKey: "2", data: "value2" },
  { partitionKey: targetPartition, rowKey: "3", data: "value3" }
];

// Add operations with validation
entities.forEach(entity => {
  if (entity.partitionKey !== targetPartition) {
    throw new Error(`Entity ${entity.rowKey} has wrong partition key`);
  }
  transaction.createEntity(entity);
});

try {
  const result = await tableClient.submitTransaction(transaction.actions);
  
  // Check for any failed operations
  const failures = result.subResponses.filter(r => r.status >= 400);
  if (failures.length > 0) {
    console.log(`${failures.length} operations failed in transaction`);
    failures.forEach((failure, index) => {
      console.log(`Failed operation ${index}: ${failure.error?.message}`);
    });
  } else {
    console.log("All operations succeeded");
  }
} catch (error) {
  console.error("Transaction failed:", error.message);
}

Manual Transaction Building

You can also build transactions manually without the helper class:

// Manual transaction action construction using tuple format
const actions: TransactionAction[] = [
  ["create", {
    partitionKey: "manual",
    rowKey: "entity1", 
    value: "created manually"
  }],
  ["delete", {
    partitionKey: "manual",
    rowKey: "entity2"
  }],
  ["update", {
    partitionKey: "manual",
    rowKey: "entity3",
    etag: "existing-etag",
    value: "updated manually"
  }, "Replace"]
];

const result = await tableClient.submitTransaction(actions);

Common Types

interface TableEntity<T extends object = Record<string, unknown>> {
  /** Required partition key for entity grouping */
  partitionKey: string;
  /** Required row key for unique identification within partition */
  rowKey: string;
  /** Entity tag for concurrency control */
  etag?: string;
}

interface HttpResponse {
  /** HTTP status code */
  status: number;
  /** Response headers */
  headers: { [key: string]: string };
  /** Response body */
  body?: any;
}