23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
595 lines
20 KiB
TypeScript
595 lines
20 KiB
TypeScript
/**
|
|
* RuVector PostgreSQL Bridge - Transactions Example
|
|
*
|
|
* This example demonstrates:
|
|
* - Multi-vector atomic updates using transactions
|
|
* - Savepoint usage for partial rollbacks
|
|
* - Error recovery strategies
|
|
* - Batch operations within transactions
|
|
*
|
|
* Run with: npx ts-node examples/ruvector/transactions.ts
|
|
*
|
|
* @module @claude-flow/plugins/examples/ruvector/transactions
|
|
*/
|
|
|
|
import {
|
|
createRuVectorBridge,
|
|
type RuVectorBridge,
|
|
type VectorRecord,
|
|
} from '../../src/integrations/ruvector/index.js';
|
|
|
|
// ============================================================================
|
|
// Configuration
|
|
// ============================================================================
|
|
|
|
const config = {
|
|
connection: {
|
|
host: process.env.POSTGRES_HOST || 'localhost',
|
|
port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
|
|
database: process.env.POSTGRES_DB || 'vectors',
|
|
user: process.env.POSTGRES_USER || 'postgres',
|
|
password: process.env.POSTGRES_PASSWORD || 'postgres',
|
|
},
|
|
dimensions: 128,
|
|
};
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Generate a random normalized embedding.
|
|
*/
|
|
function generateEmbedding(dim: number): number[] {
|
|
const vec = Array.from({ length: dim }, () => Math.random() * 2 - 1);
|
|
const mag = Math.sqrt(vec.reduce((s, v) => s + v * v, 0));
|
|
return vec.map(v => v / mag);
|
|
}
|
|
|
|
/**
|
|
* Sleep for a specified duration.
|
|
*/
|
|
function sleep(ms: number): Promise<void> {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
// ============================================================================
|
|
// Transaction Wrapper Types
|
|
// ============================================================================
|
|
|
|
interface TransactionContext {
|
|
id: string;
|
|
startTime: number;
|
|
operations: Array<{
|
|
type: string;
|
|
target: string;
|
|
success: boolean;
|
|
duration: number;
|
|
}>;
|
|
}
|
|
|
|
interface TransactionResult<T> {
|
|
success: boolean;
|
|
result?: T;
|
|
error?: Error;
|
|
context: TransactionContext;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Transaction Manager
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Transaction manager for atomic vector operations.
|
|
* Note: This is a simulation - in production, use PostgreSQL's native transactions.
|
|
*/
|
|
class VectorTransactionManager {
|
|
private bridge: RuVectorBridge;
|
|
private activeTransactions: Map<string, TransactionContext> = new Map();
|
|
private savepointStack: Map<string, Array<{ name: string; snapshot: Map<string, VectorRecord> }>> = new Map();
|
|
|
|
constructor(bridge: RuVectorBridge) {
|
|
this.bridge = bridge;
|
|
}
|
|
|
|
/**
|
|
* Begin a new transaction.
|
|
*/
|
|
async begin(): Promise<TransactionContext> {
|
|
const txId = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
const context: TransactionContext = {
|
|
id: txId,
|
|
startTime: Date.now(),
|
|
operations: [],
|
|
};
|
|
|
|
this.activeTransactions.set(txId, context);
|
|
this.savepointStack.set(txId, []);
|
|
|
|
console.log(` [TX ${txId.slice(0, 12)}] Transaction started`);
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* Create a savepoint within the transaction.
|
|
*/
|
|
async savepoint(context: TransactionContext, name: string, collection: string): Promise<void> {
|
|
const stack = this.savepointStack.get(context.id);
|
|
if (!stack) throw new Error('Transaction not found');
|
|
|
|
// Capture current state (simplified - in production, PostgreSQL handles this)
|
|
const snapshot = new Map<string, VectorRecord>();
|
|
|
|
// This is a simulation - in production, use SAVEPOINT SQL command
|
|
stack.push({ name, snapshot });
|
|
|
|
console.log(` [TX ${context.id.slice(0, 12)}] Savepoint '${name}' created`);
|
|
}
|
|
|
|
/**
|
|
* Rollback to a savepoint.
|
|
*/
|
|
async rollbackToSavepoint(context: TransactionContext, name: string): Promise<void> {
|
|
const stack = this.savepointStack.get(context.id);
|
|
if (!stack) throw new Error('Transaction not found');
|
|
|
|
// Find and remove savepoints up to and including the named one
|
|
let found = false;
|
|
while (stack.length > 0) {
|
|
const sp = stack.pop()!;
|
|
if (sp.name === name) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
throw new Error(`Savepoint '${name}' not found`);
|
|
}
|
|
|
|
console.log(` [TX ${context.id.slice(0, 12)}] Rolled back to savepoint '${name}'`);
|
|
}
|
|
|
|
/**
|
|
* Commit the transaction.
|
|
*/
|
|
async commit(context: TransactionContext): Promise<void> {
|
|
const duration = Date.now() - context.startTime;
|
|
const successOps = context.operations.filter(op => op.success).length;
|
|
|
|
this.activeTransactions.delete(context.id);
|
|
this.savepointStack.delete(context.id);
|
|
|
|
console.log(
|
|
` [TX ${context.id.slice(0, 12)}] Committed ` +
|
|
`(${successOps}/${context.operations.length} ops, ${duration}ms)`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Rollback the transaction.
|
|
*/
|
|
async rollback(context: TransactionContext, reason?: string): Promise<void> {
|
|
const duration = Date.now() - context.startTime;
|
|
|
|
this.activeTransactions.delete(context.id);
|
|
this.savepointStack.delete(context.id);
|
|
|
|
console.log(
|
|
` [TX ${context.id.slice(0, 12)}] Rolled back ` +
|
|
`(${reason || 'explicit rollback'}, ${duration}ms)`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Execute an operation within the transaction.
|
|
*/
|
|
async execute<T>(
|
|
context: TransactionContext,
|
|
operationType: string,
|
|
target: string,
|
|
operation: () => Promise<T>
|
|
): Promise<T> {
|
|
const startTime = performance.now();
|
|
let success = false;
|
|
|
|
try {
|
|
const result = await operation();
|
|
success = true;
|
|
return result;
|
|
} catch (error) {
|
|
throw error;
|
|
} finally {
|
|
const duration = performance.now() - startTime;
|
|
context.operations.push({
|
|
type: operationType,
|
|
target,
|
|
success,
|
|
duration,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run a transaction with automatic commit/rollback.
|
|
*/
|
|
async run<T>(
|
|
fn: (context: TransactionContext) => Promise<T>
|
|
): Promise<TransactionResult<T>> {
|
|
const context = await this.begin();
|
|
|
|
try {
|
|
const result = await fn(context);
|
|
await this.commit(context);
|
|
return { success: true, result, context };
|
|
} catch (error) {
|
|
await this.rollback(context, (error as Error).message);
|
|
return { success: false, error: error as Error, context };
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Main Example
|
|
// ============================================================================
|
|
|
|
async function main(): Promise<void> {
|
|
console.log('RuVector PostgreSQL Bridge - Transactions Example');
|
|
console.log('===================================================\n');
|
|
|
|
const bridge: RuVectorBridge = createRuVectorBridge({
|
|
connectionString: `postgresql://${config.connection.user}:${config.connection.password}@${config.connection.host}:${config.connection.port}/${config.connection.database}`,
|
|
poolSize: 5,
|
|
});
|
|
|
|
const txManager = new VectorTransactionManager(bridge);
|
|
|
|
try {
|
|
await bridge.connect();
|
|
console.log('Connected to PostgreSQL\n');
|
|
|
|
// ========================================================================
|
|
// 1. Create Test Collection
|
|
// ========================================================================
|
|
console.log('1. Creating test collection...');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
await bridge.createCollection('transactions_demo', {
|
|
dimensions: config.dimensions,
|
|
distanceMetric: 'cosine',
|
|
indexType: 'hnsw',
|
|
});
|
|
console.log(' Collection created\n');
|
|
|
|
// ========================================================================
|
|
// 2. Simple Transaction - All or Nothing
|
|
// ========================================================================
|
|
console.log('2. Simple Transaction (All or Nothing)');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const simpleResult = await txManager.run(async (ctx) => {
|
|
// Insert multiple vectors atomically
|
|
for (let i = 0; i < 5; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `doc_${i}`, async () => {
|
|
await bridge.insert('transactions_demo', {
|
|
id: `doc_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 1, index: i },
|
|
});
|
|
});
|
|
}
|
|
|
|
return { inserted: 5 };
|
|
});
|
|
|
|
console.log(` Result: ${simpleResult.success ? 'Committed' : 'Rolled back'}`);
|
|
console.log(` Inserted: ${simpleResult.result?.inserted || 0} vectors\n`);
|
|
|
|
// ========================================================================
|
|
// 3. Transaction with Error - Automatic Rollback
|
|
// ========================================================================
|
|
console.log('3. Transaction with Error (Automatic Rollback)');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const errorResult = await txManager.run(async (ctx) => {
|
|
// Insert some vectors
|
|
for (let i = 5; i < 8; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `doc_${i}`, async () => {
|
|
await bridge.insert('transactions_demo', {
|
|
id: `doc_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 2, index: i },
|
|
});
|
|
});
|
|
}
|
|
|
|
// Simulate an error
|
|
throw new Error('Simulated processing error');
|
|
});
|
|
|
|
console.log(` Result: ${errorResult.success ? 'Committed' : 'Rolled back'}`);
|
|
console.log(` Error: ${errorResult.error?.message}`);
|
|
console.log(` Operations attempted: ${errorResult.context.operations.length}\n`);
|
|
|
|
// ========================================================================
|
|
// 4. Transaction with Savepoints
|
|
// ========================================================================
|
|
console.log('4. Transaction with Savepoints');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const savepointResult = await txManager.run(async (ctx) => {
|
|
const results: string[] = [];
|
|
|
|
// First batch
|
|
for (let i = 10; i < 13; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `doc_${i}`, async () => {
|
|
await bridge.insert('transactions_demo', {
|
|
id: `doc_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 3, index: i },
|
|
});
|
|
});
|
|
results.push(`doc_${i}`);
|
|
}
|
|
|
|
// Create savepoint after first batch
|
|
await txManager.savepoint(ctx, 'after_batch_1', 'transactions_demo');
|
|
|
|
// Second batch (will be rolled back)
|
|
try {
|
|
for (let i = 13; i < 16; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `doc_${i}`, async () => {
|
|
await bridge.insert('transactions_demo', {
|
|
id: `doc_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 3, index: i },
|
|
});
|
|
|
|
// Simulate error on specific index
|
|
if (i === 14) {
|
|
throw new Error('Error in second batch');
|
|
}
|
|
});
|
|
results.push(`doc_${i}`);
|
|
}
|
|
} catch (error) {
|
|
console.log(` Error in second batch: ${(error as Error).message}`);
|
|
await txManager.rollbackToSavepoint(ctx, 'after_batch_1');
|
|
}
|
|
|
|
// Third batch (after savepoint rollback)
|
|
for (let i = 20; i < 22; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `doc_${i}`, async () => {
|
|
await bridge.insert('transactions_demo', {
|
|
id: `doc_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 3, index: i, afterRollback: true },
|
|
});
|
|
});
|
|
results.push(`doc_${i}`);
|
|
}
|
|
|
|
return { inserted: results };
|
|
});
|
|
|
|
console.log(` Result: ${savepointResult.success ? 'Committed' : 'Rolled back'}`);
|
|
console.log(` Final inserted: ${savepointResult.result?.inserted?.join(', ')}\n`);
|
|
|
|
// ========================================================================
|
|
// 5. Batch Update Transaction
|
|
// ========================================================================
|
|
console.log('5. Batch Update Transaction');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const updateResult = await txManager.run(async (ctx) => {
|
|
let updated = 0;
|
|
|
|
// Get existing documents
|
|
const searchResults = await bridge.search(
|
|
'transactions_demo',
|
|
generateEmbedding(config.dimensions),
|
|
{ k: 5, includeMetadata: true }
|
|
);
|
|
|
|
// Update each document with new embedding
|
|
for (const result of searchResults) {
|
|
await txManager.execute(ctx, 'UPDATE', result.id, async () => {
|
|
await bridge.update('transactions_demo', result.id, {
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: {
|
|
...result.metadata,
|
|
updatedAt: new Date().toISOString(),
|
|
version: ((result.metadata?.version as number) || 0) + 1,
|
|
},
|
|
});
|
|
});
|
|
updated++;
|
|
}
|
|
|
|
return { updated };
|
|
});
|
|
|
|
console.log(` Result: ${updateResult.success ? 'Committed' : 'Rolled back'}`);
|
|
console.log(` Updated: ${updateResult.result?.updated || 0} vectors\n`);
|
|
|
|
// ========================================================================
|
|
// 6. Conditional Transaction
|
|
// ========================================================================
|
|
console.log('6. Conditional Transaction (with validation)');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const conditionalResult = await txManager.run(async (ctx) => {
|
|
// Check precondition
|
|
const stats = await bridge.getCollectionStats('transactions_demo');
|
|
console.log(` Current vector count: ${stats.vectorCount}`);
|
|
|
|
if (stats.vectorCount > 100) {
|
|
throw new Error('Collection already has too many vectors');
|
|
}
|
|
|
|
// Proceed with inserts if validation passes
|
|
for (let i = 30; i < 35; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `cond_${i}`, async () => {
|
|
await bridge.insert('transactions_demo', {
|
|
id: `cond_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 'conditional', index: i },
|
|
});
|
|
});
|
|
}
|
|
|
|
return { inserted: 5 };
|
|
});
|
|
|
|
console.log(` Result: ${conditionalResult.success ? 'Committed' : 'Rolled back'}`);
|
|
if (conditionalResult.error) {
|
|
console.log(` Reason: ${conditionalResult.error.message}`);
|
|
} else {
|
|
console.log(` Inserted: ${conditionalResult.result?.inserted || 0} vectors`);
|
|
}
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 7. Retry with Exponential Backoff
|
|
// ========================================================================
|
|
console.log('7. Transaction with Retry (Exponential Backoff)');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
const maxRetries = 3;
|
|
let retryCount = 0;
|
|
let finalSuccess = false;
|
|
|
|
while (retryCount < maxRetries && !finalSuccess) {
|
|
const attemptResult = await txManager.run(async (ctx) => {
|
|
// Simulate occasional failures
|
|
const shouldFail = retryCount < 2 && Math.random() > 0.5;
|
|
|
|
for (let i = 40; i < 42; i++) {
|
|
await txManager.execute(ctx, 'INSERT', `retry_${i}`, async () => {
|
|
if (shouldFail && i === 41) {
|
|
throw new Error('Transient error');
|
|
}
|
|
|
|
await bridge.insert('transactions_demo', {
|
|
id: `retry_${retryCount}_${i}`,
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { batch: 'retry', attempt: retryCount },
|
|
});
|
|
});
|
|
}
|
|
|
|
return { inserted: 2 };
|
|
});
|
|
|
|
if (attemptResult.success) {
|
|
finalSuccess = true;
|
|
console.log(` Success on attempt ${retryCount + 1}`);
|
|
} else {
|
|
retryCount++;
|
|
if (retryCount < maxRetries) {
|
|
const backoffMs = Math.pow(2, retryCount) * 100;
|
|
console.log(` Attempt ${retryCount} failed, retrying in ${backoffMs}ms...`);
|
|
await sleep(backoffMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(` Final result: ${finalSuccess ? 'Success' : 'Failed after ' + maxRetries + ' attempts'}\n`);
|
|
|
|
// ========================================================================
|
|
// 8. Optimistic Locking Pattern
|
|
// ========================================================================
|
|
console.log('8. Optimistic Locking Pattern');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
// Insert a document with version
|
|
await bridge.insert('transactions_demo', {
|
|
id: 'optimistic_lock_test',
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: { version: 1, data: 'initial' },
|
|
});
|
|
|
|
const optimisticResult = await txManager.run(async (ctx) => {
|
|
// Read current version
|
|
const current = await bridge.get('transactions_demo', 'optimistic_lock_test');
|
|
if (!current) throw new Error('Document not found');
|
|
|
|
const currentVersion = (current.metadata?.version as number) || 0;
|
|
console.log(` Current version: ${currentVersion}`);
|
|
|
|
// Simulate concurrent modification check
|
|
// In production, use a WHERE clause with version check
|
|
const expectedVersion = currentVersion;
|
|
|
|
// Update with version increment
|
|
await txManager.execute(ctx, 'UPDATE', 'optimistic_lock_test', async () => {
|
|
// Verify version hasn't changed (optimistic lock check)
|
|
const recheck = await bridge.get('transactions_demo', 'optimistic_lock_test');
|
|
if ((recheck?.metadata?.version as number) !== expectedVersion) {
|
|
throw new Error('Optimistic lock failed: version mismatch');
|
|
}
|
|
|
|
await bridge.update('transactions_demo', 'optimistic_lock_test', {
|
|
embedding: generateEmbedding(config.dimensions),
|
|
metadata: {
|
|
version: expectedVersion + 1,
|
|
data: 'updated',
|
|
updatedAt: new Date().toISOString(),
|
|
},
|
|
});
|
|
});
|
|
|
|
return { newVersion: expectedVersion + 1 };
|
|
});
|
|
|
|
console.log(` Result: ${optimisticResult.success ? 'Committed' : 'Rolled back'}`);
|
|
if (optimisticResult.success) {
|
|
console.log(` New version: ${optimisticResult.result?.newVersion}`);
|
|
}
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 9. Transaction Metrics
|
|
// ========================================================================
|
|
console.log('9. Transaction Summary Metrics');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
// Collect metrics from all transactions
|
|
const stats = await bridge.getCollectionStats('transactions_demo');
|
|
|
|
console.log(` Final collection state:`);
|
|
console.log(` Total vectors: ${stats.vectorCount}`);
|
|
console.log(` Index type: ${stats.indexType}`);
|
|
console.log(` Index size: ${(stats.indexSizeBytes / 1024).toFixed(2)} KB`);
|
|
console.log();
|
|
|
|
// ========================================================================
|
|
// 10. Cleanup
|
|
// ========================================================================
|
|
console.log('10. Cleanup');
|
|
console.log(' ' + '-'.repeat(50));
|
|
|
|
// Uncomment to drop the collection
|
|
// await bridge.dropCollection('transactions_demo');
|
|
// console.log(' Collection dropped');
|
|
|
|
console.log(' Skipping collection drop (uncomment to enable)');
|
|
|
|
// ========================================================================
|
|
// Done
|
|
// ========================================================================
|
|
console.log('\n' + '='.repeat(55));
|
|
console.log('Transactions example completed!');
|
|
console.log('='.repeat(55));
|
|
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
throw error;
|
|
} finally {
|
|
await bridge.disconnect();
|
|
console.log('\nDisconnected from PostgreSQL.');
|
|
}
|
|
}
|
|
|
|
main().catch(console.error);
|