Database transaction support for ensuring data consistency across multiple operations with comprehensive ACID guarantees and streaming transaction management.
Create and manage database transactions for multi-operation consistency.
/**
* Begins a new streaming transaction
* @param collections - Collections involved in the transaction
* @param options - Transaction configuration options
* @returns Promise resolving to Transaction instance
*/
beginTransaction(
collections: TransactionCollectionOptions,
options?: TransactionOptions
): Promise<Transaction>;
/**
* Executes a function or code string within a single transaction
* @param action - Function or AQL code to execute in transaction
* @param options - Transaction execution options
* @returns Promise resolving to action result
*/
executeTransaction<T>(
action: string | TransactionFunction<T>,
options?: TransactionOptions
): Promise<T>;Usage Examples:
import { Database } from "arangojs";
const db = new Database();
// Begin streaming transaction
const trx = await db.beginTransaction({
write: ["users", "accounts"],
read: ["settings"]
}, {
waitForSync: true,
allowImplicit: false,
lockTimeout: 30,
maxTransactionSize: 100000
});
try {
// Perform operations within transaction
const user = await trx.step(() =>
db.collection("users").save({
name: "Alice Johnson",
email: "alice@example.com"
})
);
const account = await trx.step(() =>
db.collection("accounts").save({
userId: user._key,
balance: 1000.00,
currency: "USD"
})
);
// Commit transaction
await trx.commit();
console.log("Transaction committed successfully");
} catch (error) {
// Abort transaction on error
await trx.abort();
console.error("Transaction aborted:", error);
}
// Execute function in single transaction
const result = await db.executeTransaction(
async (collections) => {
const users = collections.users;
const posts = collections.posts;
// Create user
const user = await users.save({
name: "Bob Wilson",
email: "bob@example.com"
});
// Create initial post
const post = await posts.save({
authorId: user._key,
title: "Hello World",
content: "My first post!"
});
return { user, post };
},
{
write: ["users", "posts"],
waitForSync: true
}
);
console.log("Created user and post:", result);Core transaction lifecycle operations and status management.
/**
* Transaction class for managing streaming transactions
*/
class Transaction {
/** Transaction ID */
readonly id: string;
/** Transaction status */
readonly status: TransactionStatus;
/**
* Checks if the transaction exists and is active
* @returns Promise resolving to boolean indicating existence
*/
exists(): Promise<boolean>;
/**
* Gets current transaction information and status
* @returns Promise resolving to transaction info
*/
get(): Promise<TransactionInfo>;
/**
* Commits the transaction, making all changes permanent
* @param options - Commit options
* @returns Promise resolving to transaction info
*/
commit(options?: TransactionCommitOptions): Promise<TransactionInfo>;
/**
* Aborts the transaction, rolling back all changes
* @param options - Abort options
* @returns Promise resolving to transaction info
*/
abort(options?: TransactionAbortOptions): Promise<TransactionInfo>;
/**
* Executes a function as a step within the transaction
* @param callback - Function to execute in transaction context
* @returns Promise resolving to callback result
*/
step<T>(callback: () => Promise<T>): Promise<T>;
/**
* Runs an action within the transaction context
* @param action - Action to execute
* @returns Promise resolving to action result
*/
run<T>(action: TransactionAction<T>): Promise<T>;
}Usage Examples:
const trx = await db.beginTransaction({
write: ["orders", "inventory", "customers"]
});
// Check transaction status
const info = await trx.get();
console.log("Transaction status:", info.status);
console.log("Transaction size:", info.sizeInBytes);
// Execute multiple operations as steps
try {
// Step 1: Create customer
const customer = await trx.step(async () => {
return await db.collection("customers").save({
name: "John Doe",
email: "john@example.com",
address: "123 Main St"
});
});
// Step 2: Check inventory
const inventoryItem = await trx.step(async () => {
return await db.collection("inventory").document("product-123");
});
if (inventoryItem.quantity < 1) {
throw new Error("Insufficient inventory");
}
// Step 3: Create order
const order = await trx.step(async () => {
return await db.collection("orders").save({
customerId: customer._key,
productId: "product-123",
quantity: 1,
price: inventoryItem.price,
status: "confirmed"
});
});
// Step 4: Update inventory
await trx.step(async () => {
await db.collection("inventory").update("product-123", {
quantity: inventoryItem.quantity - 1,
lastSold: new Date()
});
});
// Commit all changes
const commitResult = await trx.commit();
console.log("Order transaction committed:", commitResult.id);
} catch (error) {
// Abort on any error
await trx.abort();
console.error("Order transaction aborted:", error.message);
}Execute single operations within transaction contexts for simpler use cases.
/**
* Executes AQL query within a transaction
* @param query - AQL query to execute
* @param options - Transaction and query options
* @returns Promise resolving to query cursor
*/
queryTransaction<T>(
query: AqlQuery<T>,
options?: QueryOptions & TransactionOptions
): Promise<Cursor<T>>;Usage Examples:
// Single query transaction
const results = await db.queryTransaction(aql`
LET customer = DOCUMENT("customers/cust123")
LET order = {
customerId: customer._key,
items: [
{ productId: "prod1", quantity: 2, price: 29.99 },
{ productId: "prod2", quantity: 1, price: 49.99 }
],
total: 109.97,
createdAt: DATE_NOW()
}
INSERT order INTO orders
FOR item IN order.items
UPDATE item.productId WITH {
stock: DOCUMENT("products", item.productId).stock - item.quantity,
lastSold: DATE_NOW()
} IN products
RETURN NEW
`, {
write: ["orders", "products"],
read: ["customers"],
waitForSync: true
});
const createdOrder = await results.next();
console.log("Created order:", createdOrder);
// Function-based single transaction
const transferResult = await db.executeTransaction(
`
function(params) {
const { fromAccount, toAccount, amount } = params;
// Get current balances
const from = db.accounts.document(fromAccount);
const to = db.accounts.document(toAccount);
if (from.balance < amount) {
throw new Error("Insufficient funds");
}
// Update balances
db.accounts.update(fromAccount, {
balance: from.balance - amount,
lastTransaction: Date.now()
});
db.accounts.update(toAccount, {
balance: to.balance + amount,
lastTransaction: Date.now()
});
return {
fromBalance: from.balance - amount,
toBalance: to.balance + amount,
transferAmount: amount
};
}
`,
{
write: ["accounts"],
params: {
fromAccount: "acc123",
toAccount: "acc456",
amount: 250.00
}
}
);
console.log("Transfer completed:", transferResult);Advanced transaction features including nested transactions and savepoints.
/**
* Creates a savepoint within the current transaction
* @param name - Name for the savepoint
* @returns Promise resolving to savepoint ID
*/
savepoint(name: string): Promise<string>;
/**
* Rolls back to a specific savepoint
* @param savepointId - ID of the savepoint to roll back to
* @returns Promise resolving when rollback is complete
*/
rollbackToSavepoint(savepointId: string): Promise<void>;
/**
* Gets transaction metrics and performance statistics
* @returns Promise resolving to transaction metrics
*/
getMetrics(): Promise<TransactionMetrics>;Usage Examples:
const trx = await db.beginTransaction({
write: ["users", "profiles", "settings"]
});
try {
// Create user
const user = await trx.step(async () => {
return await db.collection("users").save({
username: "newuser",
email: "user@example.com"
});
});
// Create savepoint before profile creation
const savepoint1 = await trx.savepoint("after_user_creation");
try {
// Create user profile
await trx.step(async () => {
await db.collection("profiles").save({
userId: user._key,
displayName: "New User",
avatar: "/default-avatar.png"
});
});
// Create another savepoint
const savepoint2 = await trx.savepoint("after_profile_creation");
// Try to create settings (might fail)
await trx.step(async () => {
await db.collection("settings").save({
userId: user._key,
theme: "dark",
notifications: true
});
});
} catch (profileError) {
// Roll back to after user creation
await trx.rollbackToSavepoint(savepoint1);
console.log("Profile creation failed, rolled back to user creation");
// Create minimal profile instead
await trx.step(async () => {
await db.collection("profiles").save({
userId: user._key,
displayName: user.username
});
});
}
// Get transaction metrics
const metrics = await trx.getMetrics();
console.log("Transaction size:", metrics.sizeInBytes);
console.log("Operations count:", metrics.operationsCount);
await trx.commit();
} catch (error) {
await trx.abort();
throw error;
}Monitor transaction performance and resource usage.
/**
* Lists all currently running transactions
* @returns Promise resolving to array of running transactions
*/
listRunningTransactions(): Promise<RunningTransaction[]>;
/**
* Gets transaction history and statistics
* @param options - History retrieval options
* @returns Promise resolving to transaction history
*/
getTransactionHistory(options?: TransactionHistoryOptions): Promise<TransactionHistory>;Usage Examples:
// Monitor running transactions
const runningTransactions = await db.listRunningTransactions();
for (const trx of runningTransactions) {
console.log(`Transaction ${trx.id}:`);
console.log(` Status: ${trx.status}`);
console.log(` Size: ${trx.sizeInBytes} bytes`);
console.log(` Runtime: ${trx.runTime}s`);
// Abort long-running transactions if needed
if (trx.runTime > 300) { // 5 minutes
const transaction = new Transaction(trx.id);
await transaction.abort();
console.log(`Aborted long-running transaction ${trx.id}`);
}
}
// Get transaction history for analysis
const history = await db.getTransactionHistory({
limit: 100,
minRunTime: 1.0, // Only transactions that took > 1 second
includeAborted: true
});
console.log(`Analyzed ${history.transactions.length} transactions`);
console.log(`Average runtime: ${history.averageRunTime}s`);
console.log(`Success rate: ${history.successRate * 100}%`);class Transaction {
readonly id: string;
readonly status: TransactionStatus;
exists(): Promise<boolean>;
get(): Promise<TransactionInfo>;
commit(options?: TransactionCommitOptions): Promise<TransactionInfo>;
abort(options?: TransactionAbortOptions): Promise<TransactionInfo>;
step<T>(callback: () => Promise<T>): Promise<T>;
run<T>(action: TransactionAction<T>): Promise<T>;
}
interface TransactionCollectionOptions {
read?: string | string[];
write?: string | string[];
exclusive?: string | string[];
}
interface TransactionOptions {
waitForSync?: boolean;
allowImplicit?: boolean;
lockTimeout?: number;
maxTransactionSize?: number;
intermediateCommitCount?: number;
intermediateCommitSize?: number;
skipFastLockRound?: boolean;
}
type TransactionStatus = "running" | "committed" | "aborted";
interface TransactionInfo {
id: string;
status: TransactionStatus;
sizeInBytes: number;
lockTimeout: number;
runTime: number;
state: {
running: boolean;
writing: boolean;
};
}
interface TransactionCommitOptions {
waitForSync?: boolean;
}
interface TransactionAbortOptions {
waitForSync?: boolean;
}
type TransactionFunction<T> = (
collections: Record<string, any>
) => T | Promise<T>;
type TransactionAction<T> = () => T | Promise<T>;
interface TransactionMetrics {
sizeInBytes: number;
operationsCount: number;
collectionsRead: string[];
collectionsWritten: string[];
runTime: number;
peakMemoryUsage: number;
}
interface RunningTransaction {
id: string;
database: string;
user: string;
status: TransactionStatus;
sizeInBytes: number;
runTime: number;
lockTimeout: number;
collections: {
read: string[];
write: string[];
exclusive: string[];
};
}
interface TransactionHistory {
transactions: Array<{
id: string;
status: TransactionStatus;
runTime: number;
sizeInBytes: number;
startTime: string;
endTime: string;
}>;
totalCount: number;
averageRunTime: number;
successRate: number;
}
interface TransactionHistoryOptions {
limit?: number;
offset?: number;
minRunTime?: number;
maxRunTime?: number;
includeAborted?: boolean;
startDate?: Date;
endDate?: Date;
}