Distributed Transactions
The Warehouse uses the Saga pattern to execute distributed transactions across multiple data sources. This ensures data consistency even when operations span different databases, graphs, and vector stores.
Overview
Traditional ACID transactions don't work across different database systems. The Saga pattern breaks a transaction into a sequence of local transactions, each with a compensating action (rollback) that can undo its effects if a later step fails.
[Step 1] → [Step 2] → [Step 3] → ✅ Success
↓ ↓ ↓
[Undo 1] ← [Undo 2] ← [Undo 3] ← ❌ Failure
Basic Transaction
Execute multiple operations as a single logical transaction:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.warehouse.transaction([
// Step 1: Create order in PostgreSQL
{
operation: 'insert',
from: {
type: 'database',
tag: 'orders-postgres',
entity: 'orders'
},
data: {
userId: 'user_123',
total: 99.99,
status: 'pending'
}
},
// Step 2: Create event in Neo4j graph
{
operation: 'insert',
from: {
type: 'graph',
tag: 'activity-neo4j',
entity: 'OrderEvent'
},
data: {
type: 'ORDER_PLACED',
userId: 'user_123',
timestamp: new Date().toISOString()
}
},
// Step 3: Update user behavior vector
{
operation: 'upsert',
from: {
type: 'vector',
tag: 'user-behavior',
entity: 'behaviors'
},
data: {
id: 'user_123_purchase',
vector: purchaseBehaviorVector,
metadata: { action: 'purchase', amount: 99.99 }
}
}
]);
if (result.status === 'completed') {
console.log('Transaction successful');
} else if (result.status === 'compensated') {
console.log('Transaction rolled back:', result.error);
}
Map<String, Object> result = ductape.warehouse.transaction([
// Step 1: Create order in PostgreSQL
Map.of(
"operation", "insert",
from: Map.of(
"type", "database",
"tag", "orders-postgres",
"entity", "orders"
),
data: Map.of(
"userId", "user_123",
"total", 99.99,
"status", "pending"
)
),
// Step 2: Create event in Neo4j graph
Map.of(
"operation", "insert",
from: Map.of(
"type", "graph",
"tag", "activity-neo4j",
"entity", "OrderEvent"
),
data: Map.of(
"type", "ORDER_PLACED",
"userId", "user_123",
timestamp: Instant.now().toISOString()
)
),
// Step 3: Update user behavior vector
Map.of(
"operation", "upsert",
from: Map.of(
"type", "vector",
"tag", "user-behavior",
"entity", "behaviors"
),
data: Map.of(
"id", "user_123_purchase",
vector: purchaseBehaviorVector,
metadata: Map.of( "action", "purchase", "amount", 99.99 )
)
)
]);
if (result.status === 'completed') Map.of(
System.out.println('Transaction successful');
) else if (result.status === 'compensated') Map.of(
System.out.println('Transaction rolled back:', result.error);
)
result := client.warehouse.transaction([
// Step 1: Create order in PostgreSQL
{
"operation": "insert",
from: {
"type": "database",
"tag": "orders-postgres",
"entity": "orders"
},
data: {
"userId": "user_123",
"total": 99.99,
"status": "pending"
}
},
// Step 2: Create event in Neo4j graph
{
"operation": "insert",
from: {
"type": "graph",
"tag": "activity-neo4j",
"entity": "OrderEvent"
},
data: {
"type": "ORDER_PLACED",
"userId": "user_123",
timestamp: new Date().toISOString()
}
},
// Step 3: Update user behavior vector
{
"operation": "upsert",
from: {
"type": "vector",
"tag": "user-behavior",
"entity": "behaviors"
},
data: {
"id": "user_123_purchase",
vector: purchaseBehaviorVector,
metadata: { "action": "purchase", "amount": 99.99 }
}
}
]);
if (result.status === 'completed') {
fmt.Println('Transaction successful');
} else if (result.status === 'compensated') {
fmt.Println('Transaction rolled back:', result.error);
}
var result = await ductape.warehouse.transaction([
// Step 1: Create order in PostgreSQL
{
["operation"] = "insert",
from: {
["type"] = "database",
["tag"] = "orders-postgres",
["entity"] = "orders"
},
data: {
["userId"] = "user_123",
["total"] = 99.99,
["status"] = "pending"
}
},
// Step 2: Create event in Neo4j graph
{
["operation"] = "insert",
from: {
["type"] = "graph",
["tag"] = "activity-neo4j",
["entity"] = "OrderEvent"
},
data: {
["type"] = "ORDER_PLACED",
["userId"] = "user_123",
timestamp: DateTime.UtcNow.toISOString()
}
},
// Step 3: Update user behavior vector
{
["operation"] = "upsert",
from: {
["type"] = "vector",
["tag"] = "user-behavior",
["entity"] = "behaviors"
},
data: {
["id"] = "user_123_purchase",
vector: purchaseBehaviorVector,
metadata: { ["action"] = "purchase", ["amount"] = 99.99 }
}
}
]);
if (result.status === 'completed') {
Console.WriteLine('Transaction successful');
} else if (result.status === 'compensated') {
Console.WriteLine('Transaction rolled back:', result.error);
}
Transaction Result
interface ISagaResult {
status: 'completed' | 'compensated' | 'failed';
executionTime: number;
stepResults: Record<string, {
data?: any[];
affectedRows?: number;
error?: string;
}>;
compensatedSteps?: string[];
error?: string;
}
Transaction Options
Configure transaction behavior with options:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.warehouse.transaction(
operations,
{
timeout: 30000, // Total timeout in ms
retryOnFailure: true, // Retry failed steps
maxRetries: 3, // Max retry attempts per step
compensateOnTimeout: true // Rollback if timeout
}
);
Map<String, Object> result = ductape.warehouse.transaction(
operations,
Map.of(
"timeout", 30000, // Total timeout in ms
"retryOnFailure", true, // Retry failed steps
"maxRetries", 3, // Max retry attempts per step
"compensateOnTimeout", true // Rollback if timeout
)
);
result := client.warehouse.transaction(
operations,
{
"timeout": 30000, // Total timeout in ms
"retryOnFailure": true, // Retry failed steps
"maxRetries": 3, // Max retry attempts per step
"compensateOnTimeout": true // Rollback if timeout
}
);
var result = await ductape.warehouse.transaction(
operations,
{
["timeout"] = 30000, // Total timeout in ms
["retryOnFailure"] = true, // Retry failed steps
["maxRetries"] = 3, // Max retry attempts per step
["compensateOnTimeout"] = true // Rollback if timeout
}
);
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 30000 | Maximum execution time in ms |
retryOnFailure | boolean | false | Auto-retry failed steps |
maxRetries | number | 3 | Max retries per step |
compensateOnTimeout | boolean | true | Rollback on timeout |
Multi-Database Write
A common pattern - write to multiple databases atomically:
- TypeScript
- Java
- Go
- .NET
// Create a user across multiple systems
const result = await ductape.warehouse.transaction([
// 1. Create user in main database
{
operation: 'insert',
from: {
type: 'database',
tag: 'users-postgres',
entity: 'users'
},
data: {
id: userId,
email: 'john@example.com',
name: 'John Doe',
createdAt: new Date()
}
},
// 2. Create user profile in MongoDB
{
operation: 'insert',
from: {
type: 'database',
tag: 'profiles-mongo',
entity: 'profiles'
},
data: {
userId,
bio: '',
avatar: null,
preferences: defaultPreferences
}
},
// 3. Create user node in graph
{
operation: 'insert',
from: {
type: 'graph',
tag: 'social-neo4j',
entity: 'User'
},
data: {
id: userId,
name: 'John Doe',
joinedAt: new Date().toISOString()
}
},
// 4. Index user for search
{
operation: 'upsert',
from: {
type: 'vector',
tag: 'user-search',
entity: 'users'
},
data: {
id: userId,
vector: await generateUserEmbedding({ name: 'John Doe', email: 'john@example.com' }),
metadata: { name: 'John Doe', email: 'john@example.com' }
}
}
]);
// Create a user across multiple systems
Map<String, Object> result = ductape.warehouse.transaction([
// 1. Create user in main database
Map.of(
"operation", "insert",
from: Map.of(
"type", "database",
"tag", "users-postgres",
"entity", "users"
),
data: Map.of(
id: userId,
"email", "john@example.com",
"name", "John Doe",
createdAt: Instant.now()
)
),
// 2. Create user profile in MongoDB
Map.of(
"operation", "insert",
from: Map.of(
"type", "database",
"tag", "profiles-mongo",
"entity", "profiles"
),
data: Map.of(
userId,
"bio", "",
avatar: null,
preferences: defaultPreferences
)
),
// 3. Create user node in graph
Map.of(
"operation", "insert",
from: Map.of(
"type", "graph",
"tag", "social-neo4j",
"entity", "User"
),
data: Map.of(
id: userId,
"name", "John Doe",
joinedAt: Instant.now().toISOString()
)
),
// 4. Index user for search
Map.of(
"operation", "upsert",
from: Map.of(
"type", "vector",
"tag", "user-search",
"entity", "users"
),
data: Map.of(
id: userId,
vector: generateUserEmbedding(Map.of( "name", "John Doe", "email", "john@example.com" )),
metadata: Map.of( "name", "John Doe", "email", "john@example.com" )
)
)
]);
// Create a user across multiple systems
result := client.warehouse.transaction([
// 1. Create user in main database
{
"operation": "insert",
from: {
"type": "database",
"tag": "users-postgres",
"entity": "users"
},
data: {
id: userId,
"email": "john@example.com",
"name": "John Doe",
createdAt: new Date()
}
},
// 2. Create user profile in MongoDB
{
"operation": "insert",
from: {
"type": "database",
"tag": "profiles-mongo",
"entity": "profiles"
},
data: {
userId,
"bio": "",
avatar: null,
preferences: defaultPreferences
}
},
// 3. Create user node in graph
{
"operation": "insert",
from: {
"type": "graph",
"tag": "social-neo4j",
"entity": "User"
},
data: {
id: userId,
"name": "John Doe",
joinedAt: new Date().toISOString()
}
},
// 4. Index user for search
{
"operation": "upsert",
from: {
"type": "vector",
"tag": "user-search",
"entity": "users"
},
data: {
id: userId,
vector: generateUserEmbedding({ "name": "John Doe", "email": "john@example.com" }),
metadata: { "name": "John Doe", "email": "john@example.com" }
}
}
]);
// Create a user across multiple systems
var result = await ductape.warehouse.transaction([
// 1. Create user in main database
{
["operation"] = "insert",
from: {
["type"] = "database",
["tag"] = "users-postgres",
["entity"] = "users"
},
data: {
id: userId,
["email"] = "john@example.com",
["name"] = "John Doe",
createdAt: DateTime.UtcNow
}
},
// 2. Create user profile in MongoDB
{
["operation"] = "insert",
from: {
["type"] = "database",
["tag"] = "profiles-mongo",
["entity"] = "profiles"
},
data: {
userId,
["bio"] = "",
avatar: null,
preferences: defaultPreferences
}
},
// 3. Create user node in graph
{
["operation"] = "insert",
from: {
["type"] = "graph",
["tag"] = "social-neo4j",
["entity"] = "User"
},
data: {
id: userId,
["name"] = "John Doe",
joinedAt: DateTime.UtcNow.toISOString()
}
},
// 4. Index user for search
{
["operation"] = "upsert",
from: {
["type"] = "vector",
["tag"] = "user-search",
["entity"] = "users"
},
data: {
id: userId,
vector: await generateUserEmbedding({ ["name"] = "John Doe", ["email"] = "john@example.com" }),
metadata: { ["name"] = "John Doe", ["email"] = "john@example.com" }
}
}
]);
Order Processing Example
Complete order processing with inventory, payment, and notification:
- TypeScript
- Java
- Go
- .NET
async function processOrder(order: Order) {
const result = await ductape.warehouse.transaction([
// 1. Create order record
{
operation: 'insert',
from: { type: 'database', tag: 'orders-db', entity: 'orders' },
data: {
id: order.id,
userId: order.userId,
items: order.items,
total: order.total,
status: 'processing'
}
},
// 2. Decrement inventory for each item
...order.items.map(item => ({
operation: 'update' as const,
from: { type: 'database', tag: 'inventory-db', entity: 'inventory' },
data: { quantity: { $decrement: item.quantity } },
where: { productId: { $eq: item.productId } }
})),
// 3. Create payment record
{
operation: 'insert',
from: { type: 'database', tag: 'payments-db', entity: 'payments' },
data: {
orderId: order.id,
amount: order.total,
status: 'pending',
method: order.paymentMethod
}
},
// 4. Log order event in graph
{
operation: 'insert',
from: { type: 'graph', tag: 'events-neo4j', entity: 'OrderEvent' },
data: {
orderId: order.id,
userId: order.userId,
type: 'ORDER_CREATED',
timestamp: new Date().toISOString()
}
}
]);
if (result.status !== 'completed') {
throw new Error(`Order failed: ${result.error}`);
}
return result;
}
async function processOrder(order: Order) Map.of(
Map<String, Object> result = ductape.warehouse.transaction([
// 1. Create order record
Map.of(
"operation", "insert",
from: Map.of( "type", "database", "tag", "orders-db", "entity", "orders" ),
data: Map.of(
id: order.id,
userId: order.userId,
items: order.items,
total: order.total,
"status", "processing"
)
),
// 2. Decrement inventory for each item
...order.items.map(item => (Map.of(
"operation", "update" as const,
from: Map.of( "type", "database", "tag", "inventory-db", "entity", "inventory" ),
data: Map.of( quantity: Map.of( $decrement: item.quantity ) ),
where: Map.of( productId: Map.of( $eq: item.productId ) )
))),
// 3. Create payment record
Map.of(
"operation", "insert",
from: Map.of( "type", "database", "tag", "payments-db", "entity", "payments" ),
data: Map.of(
orderId: order.id,
amount: order.total,
"status", "pending",
method: order.paymentMethod
)
),
// 4. Log order event in graph
Map.of(
"operation", "insert",
from: Map.of( "type", "graph", "tag", "events-neo4j", "entity", "OrderEvent" ),
data: Map.of(
orderId: order.id,
userId: order.userId,
"type", "ORDER_CREATED",
timestamp: Instant.now().toISOString()
)
)
]);
if (result.status !== 'completed') Map.of(
throw new Error(`Order failed: $Map.of(result.error)`);
)
return result;
)
async function processOrder(order: Order) {
result := client.warehouse.transaction([
// 1. Create order record
{
"operation": "insert",
from: { "type": "database", "tag": "orders-db", "entity": "orders" },
data: {
id: order.id,
userId: order.userId,
items: order.items,
total: order.total,
"status": "processing"
}
},
// 2. Decrement inventory for each item
...order.items.map(item => ({
"operation": "update" as const,
from: { "type": "database", "tag": "inventory-db", "entity": "inventory" },
data: { quantity: { $decrement: item.quantity } },
where: { productId: { $eq: item.productId } }
})),
// 3. Create payment record
{
"operation": "insert",
from: { "type": "database", "tag": "payments-db", "entity": "payments" },
data: {
orderId: order.id,
amount: order.total,
"status": "pending",
method: order.paymentMethod
}
},
// 4. Log order event in graph
{
"operation": "insert",
from: { "type": "graph", "tag": "events-neo4j", "entity": "OrderEvent" },
data: {
orderId: order.id,
userId: order.userId,
"type": "ORDER_CREATED",
timestamp: new Date().toISOString()
}
}
]);
if (result.status !== 'completed') {
throw new Error(`Order failed: ${result.error}`);
}
return result;
}
async function processOrder(order: Order) {
var result = await ductape.warehouse.transaction([
// 1. Create order record
{
["operation"] = "insert",
from: { ["type"] = "database", ["tag"] = "orders-db", ["entity"] = "orders" },
data: {
id: order.id,
userId: order.userId,
items: order.items,
total: order.total,
["status"] = "processing"
}
},
// 2. Decrement inventory for each item
...order.items.map(item => ({
["operation"] = "update" as const,
from: { ["type"] = "database", ["tag"] = "inventory-db", ["entity"] = "inventory" },
data: { quantity: { $decrement: item.quantity } },
where: { productId: { $eq: item.productId } }
})),
// 3. Create payment record
{
["operation"] = "insert",
from: { ["type"] = "database", ["tag"] = "payments-db", ["entity"] = "payments" },
data: {
orderId: order.id,
amount: order.total,
["status"] = "pending",
method: order.paymentMethod
}
},
// 4. Log order event in graph
{
["operation"] = "insert",
from: { ["type"] = "graph", ["tag"] = "events-neo4j", ["entity"] = "OrderEvent" },
data: {
orderId: order.id,
userId: order.userId,
["type"] = "ORDER_CREATED",
timestamp: DateTime.UtcNow.toISOString()
}
}
]);
if (result.status !== 'completed') {
throw new Error(`Order failed: ${result.error}`);
}
return result;
}
Handling Failures
When a step fails, the Warehouse automatically compensates by undoing completed steps:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.warehouse.transaction([
{ operation: 'insert', from: { type: 'database', tag: 'db1', entity: 'table1' }, data: { id: 1 } },
{ operation: 'insert', from: { type: 'database', tag: 'db2', entity: 'table2' }, data: { id: 1 } },
{ operation: 'insert', from: { type: 'database', tag: 'db3', entity: 'table3' }, data: { invalidData: true } } // Fails
]);
if (result.status === 'compensated') {
console.log('Steps that were rolled back:', result.compensatedSteps);
// ['step_0', 'step_1'] - First two inserts were deleted
console.log('Error:', result.error);
}
Map<String, Object> result = ductape.warehouse.transaction([
Map.of( "operation", "insert", from: Map.of( "type", "database", "tag", "db1", "entity", "table1" ), data: Map.of( "id", 1 ) ),
Map.of( "operation", "insert", from: Map.of( "type", "database", "tag", "db2", "entity", "table2" ), data: Map.of( "id", 1 ) ),
Map.of( "operation", "insert", from: Map.of( "type", "database", "tag", "db3", "entity", "table3" ), data: Map.of( "invalidData", true ) ) // Fails
]);
if (result.status === 'compensated') Map.of(
System.out.println('Steps that were rolled "back", ", result.compensatedSteps);
// ["step_0', 'step_1'] - First two inserts were deleted
System.out.println('Error:', result.error);
)
result := client.warehouse.transaction([
{ "operation": "insert", from: { "type": "database", "tag": "db1", "entity": "table1" }, data: { "id": 1 } },
{ "operation": "insert", from: { "type": "database", "tag": "db2", "entity": "table2" }, data: { "id": 1 } },
{ "operation": "insert", from: { "type": "database", "tag": "db3", "entity": "table3" }, data: { "invalidData": true } } // Fails
]);
if (result.status === 'compensated') {
fmt.Println('Steps that were rolled "back": ", result.compensatedSteps);
// ["step_0', 'step_1'] - First two inserts were deleted
fmt.Println('Error:', result.error);
}
var result = await ductape.warehouse.transaction([
{ ["operation"] = "insert", from: { ["type"] = "database", ["tag"] = "db1", ["entity"] = "table1" }, data: { ["id"] = 1 } },
{ ["operation"] = "insert", from: { ["type"] = "database", ["tag"] = "db2", ["entity"] = "table2" }, data: { ["id"] = 1 } },
{ ["operation"] = "insert", from: { ["type"] = "database", ["tag"] = "db3", ["entity"] = "table3" }, data: { ["invalidData"] = true } } // Fails
]);
if (result.status === 'compensated') {
Console.WriteLine('Steps that were rolled ["back"] = ", result.compensatedSteps);
// ["step_0', 'step_1'] - First two inserts were deleted
Console.WriteLine('Error:', result.error);
}
Compensation Strategies
The Warehouse uses these compensation strategies by operation type:
| Operation | Compensation |
|---|---|
insert | delete the inserted record |
update | update with original values |
delete | insert the deleted record |
upsert | Delete or restore original |
Error Handling Best Practices
- TypeScript
- Java
- Go
- .NET
try {
const result = await ductape.warehouse.transaction(operations);
switch (result.status) {
case 'completed':
// All steps succeeded
return { success: true, data: result.stepResults };
case 'compensated':
// Failed but rolled back successfully
console.error('Transaction rolled back:', result.error);
return { success: false, rolledBack: true, error: result.error };
case 'failed':
// Failed and could not fully rollback
console.error('Transaction failed with partial state:', result.error);
console.error('Compensated steps:', result.compensatedSteps);
// Manual intervention may be needed
await alertOpsTeam(result);
return { success: false, rolledBack: false, error: result.error };
}
} catch (error) {
// Network or system error
console.error('System error during transaction:', error);
throw error;
}
try Map.of(
Map<String, Object> result = ductape.warehouse.transaction(operations);
switch (result.status) Map.of(
case 'completed':
// All steps succeeded
return Map.of( "success", true, data: result.stepResults );
case 'compensated':
// Failed but rolled back successfully
console.error('Transaction rolled "back", ", result.error);
return Map.of( "success", false, "rolledBack", true, error: result.error );
case "failed':
// Failed and could not fully rollback
console.error('Transaction failed with partial "state", ", result.error);
console.error("Compensated "steps", ", result.compensatedSteps);
// Manual intervention may be needed
alertOpsTeam(result);
return Map.of( "success", false, "rolledBack", false, error: result.error );
)
) catch (error) Map.of(
// Network or system error
console.error("System error during transaction:', error);
throw error;
)
try {
result := client.warehouse.transaction(operations);
switch (result.status) {
case 'completed':
// All steps succeeded
return { "success": true, data: result.stepResults };
case 'compensated':
// Failed but rolled back successfully
console.error('Transaction rolled "back": ", result.error);
return { "success": false, "rolledBack": true, error: result.error };
case "failed':
// Failed and could not fully rollback
console.error('Transaction failed with partial "state": ", result.error);
console.error("Compensated "steps": ", result.compensatedSteps);
// Manual intervention may be needed
alertOpsTeam(result);
return { "success": false, "rolledBack": false, error: result.error };
}
} catch (error) {
// Network or system error
console.error("System error during transaction:', error);
throw error;
}
try {
var result = await ductape.warehouse.transaction(operations);
switch (result.status) {
case 'completed':
// All steps succeeded
return { ["success"] = true, data: result.stepResults };
case 'compensated':
// Failed but rolled back successfully
console.error('Transaction rolled ["back"] = ", result.error);
return { ["success"] = false, ["rolledBack"] = true, error: result.error };
case "failed':
// Failed and could not fully rollback
console.error('Transaction failed with partial ["state"] = ", result.error);
console.error("Compensated ["steps"] = ", result.compensatedSteps);
// Manual intervention may be needed
await alertOpsTeam(result);
return { ["success"] = false, ["rolledBack"] = false, error: result.error };
}
} catch (error) {
// Network or system error
console.error("System error during transaction:', error);
throw error;
}
Idempotency
For reliable retries, design operations to be idempotent:
- TypeScript
- Java
- Go
- .NET
// Good: Idempotent upsert with unique key
{
operation: 'upsert',
from: { type: 'database', tag: 'orders-db', entity: 'orders' },
data: {
idempotencyKey: `order_${userId}_${timestamp}`,
// ... order data
}
}
// Good: Conditional update
{
operation: 'update',
from: { type: 'database', tag: 'inventory-db', entity: 'inventory' },
data: { quantity: { $decrement: 1 } },
where: {
productId: { $eq: productId },
quantity: { $gte: 1 } // Only if sufficient stock
}
}
// Good: Idempotent upsert with unique key
Map.of(
"operation", "upsert",
from: Map.of( "type", "database", "tag", "orders-db", "entity", "orders" ),
data: Map.of(
idempotencyKey: `order_$Map.of(userId)_$Map.of(timestamp)`,
// ... order data
)
)
// Good: Conditional update
Map.of(
"operation", "update",
from: Map.of( "type", "database", "tag", "inventory-db", "entity", "inventory" ),
data: Map.of( quantity: Map.of( $"decrement", 1 ) ),
where: Map.of(
productId: Map.of( $eq: productId ),
quantity: Map.of( $"gte", 1 ) // Only if sufficient stock
)
)
// Good: Idempotent upsert with unique key
{
"operation": "upsert",
from: { "type": "database", "tag": "orders-db", "entity": "orders" },
data: {
idempotencyKey: `order_${userId}_${timestamp}`,
// ... order data
}
}
// Good: Conditional update
{
"operation": "update",
from: { "type": "database", "tag": "inventory-db", "entity": "inventory" },
data: { quantity: { $"decrement": 1 } },
where: {
productId: { $eq: productId },
quantity: { $"gte": 1 } // Only if sufficient stock
}
}
// Good: Idempotent upsert with unique key
{
["operation"] = "upsert",
from: { ["type"] = "database", ["tag"] = "orders-db", ["entity"] = "orders" },
data: {
idempotencyKey: `order_${userId}_${timestamp}`,
// ... order data
}
}
// Good: Conditional update
{
["operation"] = "update",
from: { ["type"] = "database", ["tag"] = "inventory-db", ["entity"] = "inventory" },
data: { quantity: { $["decrement"] = 1 } },
where: {
productId: { $eq: productId },
quantity: { $["gte"] = 1 } // Only if sufficient stock
}
}
Monitoring Transactions
Track transaction execution in the result metadata:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.warehouse.transaction(operations);
console.log('Execution time:', result.executionTime, 'ms');
console.log('Steps executed:', Object.keys(result.stepResults).length);
for (const [stepId, stepResult] of Object.entries(result.stepResults)) {
console.log(`${stepId}:`, {
success: !stepResult.error,
affectedRows: stepResult.affectedRows,
error: stepResult.error
});
}
Map<String, Object> result = ductape.warehouse.transaction(operations);
System.out.println('Execution "time", ", result.executionTime, "ms');
System.out.println('Steps executed:', Object.keys(result.stepResults).length);
for (Map<String, Object> [stepId, stepResult] of Object.entries(result.stepResults)) Map.of(
System.out.println(`$Map.of(stepId):`, Map.of(
success: !stepResult.error,
affectedRows: stepResult.affectedRows,
error: stepResult.error
));
)
result := client.warehouse.transaction(operations);
fmt.Println('Execution "time": ", result.executionTime, "ms');
fmt.Println('Steps executed:', Object.keys(result.stepResults).length);
for (const [stepId, stepResult] of Object.entries(result.stepResults)) {
fmt.Println(`${stepId}:`, {
success: !stepResult.error,
affectedRows: stepResult.affectedRows,
error: stepResult.error
});
}
var result = await ductape.warehouse.transaction(operations);
Console.WriteLine('Execution ["time"] = ", result.executionTime, "ms');
Console.WriteLine('Steps executed:', Object.keys(result.stepResults).length);
for (var [stepId, stepResult] of Object.entries(result.stepResults)) {
Console.WriteLine(`${stepId}:`, {
success: !stepResult.error,
affectedRows: stepResult.affectedRows,
error: stepResult.error
});
}
Next Steps
- Query Reference - Complete query syntax documentation
- Joins - Cross-database joins
- Getting Started - Basic warehouse usage