mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-29 10:21:34 +00:00
fix database persistence during container shutdown (#1104)
This commit is contained in:
@@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,8 +81,29 @@ 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 {
|
||||||
this.pendingSave = false;
|
if (this.activeSave === save) {
|
||||||
|
this.activeSave = null;
|
||||||
|
this.pendingSave = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user