fix database persistence during container shutdown (#1104)

This commit is contained in:
ZacharyZcR
2026-07-28 01:48:11 +08:00
committed by GitHub
parent 697e363b74
commit c48bc478f8
3 changed files with 53 additions and 21 deletions
+1 -5
View File
@@ -163,8 +163,4 @@ else
echo "Warning: package.json not found" echo "Warning: package.json not found"
fi fi
node dist/backend/backend/starter.js exec node dist/backend/backend/starter.js
echo "All services started"
tail -f /dev/null
@@ -38,4 +38,28 @@ describe("DatabaseSaveTrigger", () => {
expect(DatabaseSaveTrigger.isDirty).toBe(false); expect(DatabaseSaveTrigger.isDirty).toBe(false);
expect(DatabaseSaveTrigger.getStatus().pendingSave).toBe(false); expect(DatabaseSaveTrigger.getStatus().pendingSave).toBe(false);
}); });
it("queues a force save behind an in-flight save", async () => {
let finishFirstSave: (() => void) | undefined;
const firstSave = new Promise<void>((resolve) => {
finishFirstSave = resolve;
});
const save = vi
.fn<() => Promise<void>>()
.mockReturnValueOnce(firstSave)
.mockResolvedValueOnce(undefined);
DatabaseSaveTrigger.initialize(save);
const first = DatabaseSaveTrigger.forceSave("first_write");
await vi.waitFor(() => expect(save).toHaveBeenCalledTimes(1));
const second = DatabaseSaveTrigger.forceSave("sso_provider_write");
expect(save).toHaveBeenCalledTimes(1);
finishFirstSave?.();
await Promise.all([first, second]);
expect(save).toHaveBeenCalledTimes(2);
expect(DatabaseSaveTrigger.getStatus().pendingSave).toBe(false);
});
}); });
+27 -15
View File
@@ -4,6 +4,7 @@ export class DatabaseSaveTrigger {
private static saveFunction: (() => Promise<void>) | null = null; private static saveFunction: (() => Promise<void>) | null = null;
private static isInitialized = false; private static isInitialized = false;
private static pendingSave = false; private static pendingSave = false;
private static activeSave: Promise<void> | null = null;
private static saveTimeout: NodeJS.Timeout | null = null; private static saveTimeout: NodeJS.Timeout | null = null;
private static _dirty = false; private static _dirty = false;
@@ -38,14 +39,10 @@ export class DatabaseSaveTrigger {
} }
this.saveTimeout = setTimeout(async () => { this.saveTimeout = setTimeout(async () => {
if (this.pendingSave) { this.saveTimeout = null;
return;
}
this.pendingSave = true;
try { try {
await this.saveFunction!(); await this.runSave();
this._dirty = false; this._dirty = false;
} catch (error) { } catch (error) {
databaseLogger.error("Database save failed", error, { databaseLogger.error("Database save failed", error, {
@@ -53,8 +50,6 @@ export class DatabaseSaveTrigger {
reason, reason,
error: error instanceof Error ? error.message : "Unknown error", error: error instanceof Error ? error.message : "Unknown error",
}); });
} finally {
this.pendingSave = false;
} }
}, 2000); }, 2000);
} }
@@ -76,14 +71,9 @@ export class DatabaseSaveTrigger {
this.saveTimeout = null; this.saveTimeout = null;
} }
if (this.pendingSave) {
return;
}
this.pendingSave = true;
try { try {
await this.saveFunction(); await this.runSave();
this._dirty = false;
} catch (error) { } catch (error) {
databaseLogger.error("Database force save failed", error, { databaseLogger.error("Database force save failed", error, {
operation: "db_save_trigger_force_failed", operation: "db_save_trigger_force_failed",
@@ -91,10 +81,31 @@ export class DatabaseSaveTrigger {
error: error instanceof Error ? error.message : "Unknown error", error: error instanceof Error ? error.message : "Unknown error",
}); });
throw error; throw error;
}
}
private static async runSave(): Promise<void> {
while (this.activeSave) {
try {
await this.activeSave;
} catch {
// The queued save must still run after an earlier save failed.
}
}
const save = Promise.resolve().then(() => this.saveFunction!());
this.activeSave = save;
this.pendingSave = true;
try {
await save;
} finally { } finally {
if (this.activeSave === save) {
this.activeSave = null;
this.pendingSave = false; this.pendingSave = false;
} }
} }
}
static getStatus(): { static getStatus(): {
initialized: boolean; initialized: boolean;
@@ -115,6 +126,7 @@ export class DatabaseSaveTrigger {
} }
this.pendingSave = false; this.pendingSave = false;
this.activeSave = null;
this.isInitialized = false; this.isInitialized = false;
this.saveFunction = null; this.saveFunction = null;
} }