Open source vector database with HNSW indexing, graph relationships, and metadata facets. Features CLI with professional augmentation registry integration for discovering extensions and capabilities.
126 lines
No EOL
4.5 KiB
JavaScript
126 lines
No EOL
4.5 KiB
JavaScript
/**
|
|
* Utility functions for timeout and retry logic
|
|
* Used by storage adapters to handle network operations reliably
|
|
*/
|
|
import { BrainyError } from '../errors/brainyError.js';
|
|
// Default configuration values
|
|
export const DEFAULT_TIMEOUTS = {
|
|
get: 30000, // 30 seconds
|
|
add: 60000, // 1 minute
|
|
delete: 30000 // 30 seconds
|
|
};
|
|
export const DEFAULT_RETRY_POLICY = {
|
|
maxRetries: 3,
|
|
initialDelay: 1000,
|
|
maxDelay: 10000,
|
|
backoffMultiplier: 2
|
|
};
|
|
/**
|
|
* Wraps a promise with a timeout
|
|
*/
|
|
export function withTimeout(promise, timeoutMs, operation) {
|
|
return new Promise((resolve, reject) => {
|
|
const timeoutId = setTimeout(() => {
|
|
reject(BrainyError.timeout(operation, timeoutMs));
|
|
}, timeoutMs);
|
|
promise
|
|
.then((result) => {
|
|
clearTimeout(timeoutId);
|
|
resolve(result);
|
|
})
|
|
.catch((error) => {
|
|
clearTimeout(timeoutId);
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Calculates the delay for exponential backoff
|
|
*/
|
|
function calculateBackoffDelay(attemptNumber, initialDelay, maxDelay, backoffMultiplier) {
|
|
const delay = initialDelay * Math.pow(backoffMultiplier, attemptNumber - 1);
|
|
return Math.min(delay, maxDelay);
|
|
}
|
|
/**
|
|
* Sleeps for the specified number of milliseconds
|
|
*/
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
/**
|
|
* Executes an operation with retry logic and exponential backoff
|
|
*/
|
|
export async function withRetry(operation, operationName, config = {}) {
|
|
const { maxRetries = DEFAULT_RETRY_POLICY.maxRetries, initialDelay = DEFAULT_RETRY_POLICY.initialDelay, maxDelay = DEFAULT_RETRY_POLICY.maxDelay, backoffMultiplier = DEFAULT_RETRY_POLICY.backoffMultiplier } = config;
|
|
let lastError;
|
|
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
|
|
try {
|
|
return await operation();
|
|
}
|
|
catch (error) {
|
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
// If this is the last attempt, don't retry
|
|
if (attempt > maxRetries) {
|
|
break;
|
|
}
|
|
// Check if the error is retryable
|
|
if (!BrainyError.isRetryable(lastError)) {
|
|
throw BrainyError.fromError(lastError, operationName);
|
|
}
|
|
// Calculate delay for exponential backoff
|
|
const delay = calculateBackoffDelay(attempt, initialDelay, maxDelay, backoffMultiplier);
|
|
console.warn(`Operation '${operationName}' failed on attempt ${attempt}/${maxRetries + 1}. ` +
|
|
`Retrying in ${delay}ms. Error: ${lastError.message}`);
|
|
// Wait before retrying
|
|
await sleep(delay);
|
|
}
|
|
}
|
|
// All retries exhausted
|
|
throw BrainyError.retryExhausted(operationName, maxRetries, lastError);
|
|
}
|
|
/**
|
|
* Executes an operation with both timeout and retry logic
|
|
*/
|
|
export async function withTimeoutAndRetry(operation, operationName, timeoutMs, retryConfig = {}) {
|
|
return withRetry(() => withTimeout(operation(), timeoutMs, operationName), operationName, retryConfig);
|
|
}
|
|
/**
|
|
* Creates a configured operation executor for a specific operation type
|
|
*/
|
|
export function createOperationExecutor(operationType, config = {}) {
|
|
const timeouts = { ...DEFAULT_TIMEOUTS, ...config.timeouts };
|
|
const retryPolicy = { ...DEFAULT_RETRY_POLICY, ...config.retryPolicy };
|
|
const timeoutMs = timeouts[operationType];
|
|
return async function executeOperation(operation, operationName) {
|
|
return withTimeoutAndRetry(operation, operationName, timeoutMs, retryPolicy);
|
|
};
|
|
}
|
|
/**
|
|
* Storage operation executors for different operation types
|
|
*/
|
|
export class StorageOperationExecutors {
|
|
constructor(config = {}) {
|
|
this.getExecutor = createOperationExecutor('get', config);
|
|
this.addExecutor = createOperationExecutor('add', config);
|
|
this.deleteExecutor = createOperationExecutor('delete', config);
|
|
}
|
|
/**
|
|
* Execute a get operation with timeout and retry
|
|
*/
|
|
async executeGet(operation, operationName) {
|
|
return this.getExecutor(operation, operationName);
|
|
}
|
|
/**
|
|
* Execute an add operation with timeout and retry
|
|
*/
|
|
async executeAdd(operation, operationName) {
|
|
return this.addExecutor(operation, operationName);
|
|
}
|
|
/**
|
|
* Execute a delete operation with timeout and retry
|
|
*/
|
|
async executeDelete(operation, operationName) {
|
|
return this.deleteExecutor(operation, operationName);
|
|
}
|
|
}
|
|
//# sourceMappingURL=operationUtils.js.map
|