feat(client): summarize digital sync failures

This commit is contained in:
baiyanyun
2026-06-07 01:51:15 +08:00
parent cef3fe4a43
commit 38ece7e3d3
6 changed files with 95 additions and 8 deletions

View File

@@ -45,6 +45,18 @@ export interface DigitalEmployeeSyncAttempt {
finishedAt: string;
}
export interface DigitalEmployeeSyncFailureGroup {
entityType: string;
count: number;
}
export interface DigitalEmployeeSyncFailureSummary {
total: number;
dueForRetry: number;
backoff: number;
byEntityType: DigitalEmployeeSyncFailureGroup[];
}
export interface DigitalEmployeeSyncFailure {
id: string;
entityType: string;
@@ -87,6 +99,7 @@ export interface DigitalEmployeeSyncStatus {
failed: number;
lastSyncAt: string | null;
lastSyncError: string | null;
failureSummary: DigitalEmployeeSyncFailureSummary;
recentFailures: DigitalEmployeeSyncFailure[];
}
@@ -123,6 +136,7 @@ export function getDigitalEmployeeSyncStatus(): DigitalEmployeeSyncStatus {
failed: countOutbox("failed"),
lastSyncAt,
lastSyncError,
failureSummary: readFailureSummary(),
recentFailures: readRecentFailures(config),
};
}
@@ -467,6 +481,43 @@ function countOutbox(status: string): number {
return row?.count ?? 0;
}
function readFailureSummary(): DigitalEmployeeSyncFailureSummary {
const db = getDb();
if (!db) return { total: 0, dueForRetry: 0, backoff: 0, byEntityType: [] };
const rows = db
.prepare(
`
SELECT entity_type, attempts, last_attempt_at
FROM digital_sync_outbox
WHERE status = 'failed'
`,
)
.all() as Array<{
entity_type: string;
attempts: number;
last_attempt_at: string | null;
}>;
const groups = new Map<string, number>();
let dueForRetry = 0;
for (const row of rows) {
groups.set(row.entity_type, (groups.get(row.entity_type) || 0) + 1);
const nextRetryAt = nextRetryAtForFailure(row.last_attempt_at, row.attempts);
if (!nextRetryAt || Date.now() >= Date.parse(nextRetryAt)) {
dueForRetry += 1;
}
}
return {
total: rows.length,
dueForRetry,
backoff: rows.length - dueForRetry,
byEntityType: Array.from(groups.entries())
.map(([entityType, count]) => ({ entityType, count }))
.sort((left, right) => right.count - left.count),
};
}
function readRecentFailures(
config: DigitalEmployeeSyncConfig,
limit = 3,