Fix desktop settings first-launch race
This commit is contained in:
@@ -43,6 +43,22 @@ describe("desktop-settings", () => {
|
|||||||
expect(persisted.settings).toEqual(DEFAULT_DESKTOP_SETTINGS);
|
expect(persisted.settings).toEqual(DEFAULT_DESKTOP_SETTINGS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("handles concurrent first-launch reads without racing the settings write", async () => {
|
||||||
|
const userDataPath = await createTempUserDataDir();
|
||||||
|
directories.add(userDataPath);
|
||||||
|
const store = createDesktopSettingsStore({ userDataPath });
|
||||||
|
|
||||||
|
const settings = await Promise.all(Array.from({ length: 20 }, () => store.get()));
|
||||||
|
const persisted = JSON.parse(await readFile(settingsFilePath(userDataPath), "utf8")) as {
|
||||||
|
settings: DesktopSettings;
|
||||||
|
};
|
||||||
|
const files = await readdir(userDataPath);
|
||||||
|
|
||||||
|
expect(settings).toEqual(Array.from({ length: 20 }, () => DEFAULT_DESKTOP_SETTINGS));
|
||||||
|
expect(persisted.settings).toEqual(DEFAULT_DESKTOP_SETTINGS);
|
||||||
|
expect(files).toEqual(["desktop-settings.json"]);
|
||||||
|
});
|
||||||
|
|
||||||
it("coerces invalid persisted values back to safe defaults", async () => {
|
it("coerces invalid persisted values back to safe defaults", async () => {
|
||||||
const userDataPath = await createTempUserDataDir();
|
const userDataPath = await createTempUserDataDir();
|
||||||
directories.add(userDataPath);
|
directories.add(userDataPath);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { randomUUID } from "node:crypto";
|
||||||
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
@@ -198,13 +199,19 @@ export function createDesktopSettingsStore({
|
|||||||
}): DesktopSettingsStore {
|
}): DesktopSettingsStore {
|
||||||
const filePath = path.join(userDataPath, DESKTOP_SETTINGS_FILENAME);
|
const filePath = path.join(userDataPath, DESKTOP_SETTINGS_FILENAME);
|
||||||
let cachedDocument: PersistedDesktopSettingsDocument | null = null;
|
let cachedDocument: PersistedDesktopSettingsDocument | null = null;
|
||||||
|
let persistQueue: Promise<void> = Promise.resolve();
|
||||||
|
|
||||||
async function persistDocument(document: PersistedDesktopSettingsDocument): Promise<void> {
|
async function persistDocument(document: PersistedDesktopSettingsDocument): Promise<void> {
|
||||||
await mkdir(userDataPath, { recursive: true });
|
const write = async () => {
|
||||||
const tempFilePath = `${filePath}.tmp`;
|
await mkdir(userDataPath, { recursive: true });
|
||||||
await writeFile(tempFilePath, `${JSON.stringify(document, null, 2)}\n`, "utf8");
|
const tempFilePath = `${filePath}.tmp.${process.pid}.${randomUUID()}`;
|
||||||
await rename(tempFilePath, filePath);
|
await writeFile(tempFilePath, `${JSON.stringify(document, null, 2)}\n`, "utf8");
|
||||||
cachedDocument = document;
|
await rename(tempFilePath, filePath);
|
||||||
|
cachedDocument = document;
|
||||||
|
};
|
||||||
|
const queued = persistQueue.then(write, write);
|
||||||
|
persistQueue = queued.catch(() => undefined);
|
||||||
|
await queued;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadDocument(): Promise<PersistedDesktopSettingsDocument> {
|
async function loadDocument(): Promise<PersistedDesktopSettingsDocument> {
|
||||||
|
|||||||
Reference in New Issue
Block a user