chore: add proper-lockfile for safe database read/write operations and implement retry logic for file access

This commit is contained in:
decolua
2026-03-27 10:31:35 +07:00
parent 3059df4014
commit 8759545260
15 changed files with 648 additions and 57 deletions
+8 -4
View File
@@ -1,4 +1,4 @@
import { HTTP_STATUS, RETRY_CONFIG } from "../config/runtimeConfig.js";
import { HTTP_STATUS, RETRY_CONFIG, DEFAULT_RETRY_CONFIG } from "../config/runtimeConfig.js";
import { proxyAwareFetch } from "../utils/proxyFetch.js";
/**
@@ -81,6 +81,9 @@ export class BaseExecutor {
let lastError = null;
let lastStatus = 0;
const retryAttemptsByUrl = {};
// Merge default retry config with provider-specific config
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
const url = this.buildUrl(model, stream, urlIndex, credentials);
@@ -97,10 +100,11 @@ export class BaseExecutor {
signal
}, proxyOptions);
// Retry 429 with fixed delay before falling back to next URL
if (response.status === HTTP_STATUS.RATE_LIMITED && retryAttemptsByUrl[urlIndex] < RETRY_CONFIG.maxAttempts) {
// Retry based on status code config
const maxRetries = retryConfig[response.status] || 0;
if (maxRetries > 0 && retryAttemptsByUrl[urlIndex] < maxRetries) {
retryAttemptsByUrl[urlIndex]++;
log?.debug?.("RETRY", `429 retry ${retryAttemptsByUrl[urlIndex]}/${RETRY_CONFIG.maxAttempts} after ${RETRY_CONFIG.delayMs / 1000}s`);
log?.debug?.("RETRY", `${response.status} retry ${retryAttemptsByUrl[urlIndex]}/${maxRetries} after ${RETRY_CONFIG.delayMs / 1000}s`);
await new Promise(resolve => setTimeout(resolve, RETRY_CONFIG.delayMs));
urlIndex--;
continue;
+33 -16
View File
@@ -3,6 +3,7 @@ import { PROVIDERS } from "../config/providers.js";
import { v4 as uuidv4 } from "uuid";
import { refreshKiroToken } from "../services/tokenRefresh.js";
import { proxyAwareFetch } from "../utils/proxyFetch.js";
import { HTTP_STATUS, RETRY_CONFIG, DEFAULT_RETRY_CONFIG } from "../config/runtimeConfig.js";
/**
* KiroExecutor - Executor for Kiro AI (AWS CodeWhisperer)
@@ -32,29 +33,45 @@ export class KiroExecutor extends BaseExecutor {
}
/**
* Custom execute for Kiro - handles AWS EventStream binary response
* Custom execute for Kiro - handles AWS EventStream binary response with retry support
*/
async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
const url = this.buildUrl(model, stream, 0);
const headers = this.buildHeaders(credentials, stream);
const transformedBody = this.transformRequest(model, body, stream, credentials);
// Merge default retry config with provider-specific config
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
let retryAttempts = 0;
const response = await proxyAwareFetch(url, {
method: "POST",
headers,
body: JSON.stringify(transformedBody),
signal
}, proxyOptions);
while (true) {
const headers = this.buildHeaders(credentials, stream);
const response = await proxyAwareFetch(url, {
method: "POST",
headers,
body: JSON.stringify(transformedBody),
signal
}, proxyOptions);
if (!response.ok) {
return { response, url, headers, transformedBody };
// Check if should retry based on status code
const maxRetries = retryConfig[response.status] || 0;
if (!response.ok && maxRetries > 0 && retryAttempts < maxRetries) {
retryAttempts++;
log?.debug?.("RETRY", `${response.status} retry ${retryAttempts}/${maxRetries} after ${RETRY_CONFIG.delayMs / 1000}s`);
await new Promise(resolve => setTimeout(resolve, RETRY_CONFIG.delayMs));
continue;
}
if (!response.ok) {
return { response, url, headers, transformedBody };
}
// Success - transform and return
// For Kiro, we need to transform the binary EventStream to SSE
// Create a TransformStream to convert binary to SSE text
const transformedResponse = this.transformEventStreamToSSE(response, model);
return { response: transformedResponse, url, headers, transformedBody };
}
// For Kiro, we need to transform the binary EventStream to SSE
// Create a TransformStream to convert binary to SSE text
const transformedResponse = this.transformEventStreamToSSE(response, model);
return { response: transformedResponse, url, headers, transformedBody };
}
/**