a restart mid-sync stranded accounts at syncing or queued forever
getAllSyncedAccounts only picks up status = synced, so an account whose sidecar process died between marking it syncing/queued and clearing that status again dropped out of the cron and the IDLE watcher silently, with no error to explain why background sync had stopped. Reset both on startup instead: the job runner is in-memory, so neither state can be genuinely in flight in a fresh process.
This commit is contained in:
+19
-1
@@ -1,4 +1,4 @@
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { eq, and, inArray } from 'drizzle-orm';
|
||||
import { db } from 'officerdb/db';
|
||||
import { encryptSecret, decryptSecret } from 'officerdb/crypto';
|
||||
import { emailAccounts } from './schema';
|
||||
@@ -71,6 +71,24 @@ export async function getAllSyncedAccounts(): Promise<EmailAccount[]> {
|
||||
return rows.map(open);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a `syncing` or `queued` status left behind by a process that died mid-sync.
|
||||
*
|
||||
* `getAllSyncedAccounts` only picks up `status = 'synced'`, so an account stranded at either — the
|
||||
* resync route sets `syncing` before awaiting and only clears it after; a first sync sets `queued`
|
||||
* before handing off to the in-process job runner — silently drops out of both the cron and the IDLE
|
||||
* watcher forever. A fresh process can't have either genuinely in flight (the job runner is in-memory,
|
||||
* not persisted), so on startup that state only ever means "orphaned by the last restart." `performResync`
|
||||
* — what the cron and IDLE watcher call — already handles an account with no prior sync state by doing a
|
||||
* full fetch, so resetting to `synced` rather than replaying the dedicated first-sync job is safe.
|
||||
*/
|
||||
export async function resetStuckSyncingAccounts(): Promise<void> {
|
||||
await db
|
||||
.update(emailAccounts)
|
||||
.set({ status: 'synced' })
|
||||
.where(inArray(emailAccounts.status, ['syncing', 'queued']));
|
||||
}
|
||||
|
||||
/** Replace an account's stored secret — used when an OAuth token is refreshed. */
|
||||
export async function updateEmailAccountCredentials(id: number, credentials: Record<string, unknown>): Promise<void> {
|
||||
await db.update(emailAccounts).set({ credentials: seal(credentials) }).where(eq(emailAccounts.id, id));
|
||||
|
||||
Reference in New Issue
Block a user