refactor: use setTimeout chain instead of setInterval for workspace sync
- Next sync schedules AFTER current completes (natural backpressure) - No more syncInProgress mutex needed - Cleaner, more predictable timing
This commit is contained in:
parent
65f135e1a3
commit
a3eaec5f41
@ -25,28 +25,50 @@ type SyncManagerLogger = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type SyncManagerState = {
|
type SyncManagerState = {
|
||||||
intervalId: ReturnType<typeof setInterval> | null;
|
timeoutId: ReturnType<typeof setTimeout> | null;
|
||||||
lastSyncAt: Date | null;
|
lastSyncAt: Date | null;
|
||||||
lastSyncOk: boolean | null;
|
lastSyncOk: boolean | null;
|
||||||
syncCount: number;
|
syncCount: number;
|
||||||
errorCount: number;
|
errorCount: number;
|
||||||
hasSuccessfulSync: boolean;
|
hasSuccessfulSync: boolean;
|
||||||
syncInProgress: boolean;
|
running: boolean;
|
||||||
|
intervalMs: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const state: SyncManagerState = {
|
const state: SyncManagerState = {
|
||||||
intervalId: null,
|
timeoutId: null,
|
||||||
lastSyncAt: null,
|
lastSyncAt: null,
|
||||||
lastSyncOk: null,
|
lastSyncOk: null,
|
||||||
syncCount: 0,
|
syncCount: 0,
|
||||||
errorCount: 0,
|
errorCount: 0,
|
||||||
hasSuccessfulSync: false,
|
hasSuccessfulSync: false,
|
||||||
syncInProgress: false,
|
running: false,
|
||||||
|
intervalMs: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let currentConfig: MoltbotConfig | null = null;
|
let currentConfig: MoltbotConfig | null = null;
|
||||||
let currentLogger: SyncManagerLogger | null = null;
|
let currentLogger: SyncManagerLogger | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule the next sync after the configured interval.
|
||||||
|
* Uses setTimeout chain pattern - next sync schedules AFTER current completes.
|
||||||
|
*/
|
||||||
|
function scheduleNextSync(): void {
|
||||||
|
if (!state.running || state.intervalMs <= 0) return;
|
||||||
|
|
||||||
|
state.timeoutId = setTimeout(() => {
|
||||||
|
runSyncLoop();
|
||||||
|
}, state.intervalMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run sync and schedule next. This is the main loop.
|
||||||
|
*/
|
||||||
|
async function runSyncLoop(): Promise<void> {
|
||||||
|
await runSync();
|
||||||
|
scheduleNextSync();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a single sync operation.
|
* Run a single sync operation.
|
||||||
* This is a pure rclone operation - no agent/LLM involvement.
|
* This is a pure rclone operation - no agent/LLM involvement.
|
||||||
@ -57,14 +79,7 @@ async function runSync(): Promise<void> {
|
|||||||
const syncConfig = currentConfig.workspace?.sync;
|
const syncConfig = currentConfig.workspace?.sync;
|
||||||
if (!syncConfig?.provider || syncConfig.provider === "off") return;
|
if (!syncConfig?.provider || syncConfig.provider === "off") return;
|
||||||
|
|
||||||
// Skip if a sync is already in progress
|
|
||||||
if (state.syncInProgress) {
|
|
||||||
currentLogger.info("[workspace-sync] Sync already in progress, skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const logger = currentLogger;
|
const logger = currentLogger;
|
||||||
state.syncInProgress = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if rclone is available
|
// Check if rclone is available
|
||||||
@ -146,8 +161,6 @@ async function runSync(): Promise<void> {
|
|||||||
logger.error(
|
logger.error(
|
||||||
`[workspace-sync] Periodic sync error: ${err instanceof Error ? err.message : String(err)}`,
|
`[workspace-sync] Periodic sync error: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
);
|
);
|
||||||
} finally {
|
|
||||||
state.syncInProgress = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,15 +227,14 @@ export function startWorkspaceSyncManager(cfg: MoltbotConfig, logger: SyncManage
|
|||||||
`[workspace-sync] Starting periodic sync every ${effectiveInterval}s (pure file sync, zero LLM cost)`,
|
`[workspace-sync] Starting periodic sync every ${effectiveInterval}s (pure file sync, zero LLM cost)`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Run initial sync after a short delay (let gateway fully start)
|
state.running = true;
|
||||||
setTimeout(() => {
|
state.intervalMs = effectiveInterval * 1000;
|
||||||
runSync().catch(() => {});
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
// Set up periodic sync
|
// Run initial sync after a short delay (let gateway fully start)
|
||||||
state.intervalId = setInterval(() => {
|
// Then schedule subsequent syncs via setTimeout chain
|
||||||
runSync().catch(() => {});
|
state.timeoutId = setTimeout(() => {
|
||||||
}, effectiveInterval * 1000);
|
runSyncLoop();
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,9 +242,10 @@ export function startWorkspaceSyncManager(cfg: MoltbotConfig, logger: SyncManage
|
|||||||
* Called when the gateway stops.
|
* Called when the gateway stops.
|
||||||
*/
|
*/
|
||||||
export function stopWorkspaceSyncManager(): void {
|
export function stopWorkspaceSyncManager(): void {
|
||||||
if (state.intervalId) {
|
state.running = false;
|
||||||
clearInterval(state.intervalId);
|
if (state.timeoutId) {
|
||||||
state.intervalId = null;
|
clearTimeout(state.timeoutId);
|
||||||
|
state.timeoutId = null;
|
||||||
}
|
}
|
||||||
currentConfig = null;
|
currentConfig = null;
|
||||||
currentLogger = null;
|
currentLogger = null;
|
||||||
@ -249,7 +262,7 @@ export function getWorkspaceSyncStatus(): {
|
|||||||
errorCount: number;
|
errorCount: number;
|
||||||
} {
|
} {
|
||||||
return {
|
return {
|
||||||
running: state.intervalId !== null,
|
running: state.running,
|
||||||
lastSyncAt: state.lastSyncAt,
|
lastSyncAt: state.lastSyncAt,
|
||||||
lastSyncOk: state.lastSyncOk,
|
lastSyncOk: state.lastSyncOk,
|
||||||
syncCount: state.syncCount,
|
syncCount: state.syncCount,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user