Skip to content

Commit

Permalink
ファイル保存全般をアトミックにする (VOICEVOX#2308)
Browse files Browse the repository at this point in the history
  • Loading branch information
tsunekazuomija authored Oct 27, 2024
1 parent 54b0640 commit cb58a10
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 14 deletions.
13 changes: 2 additions & 11 deletions src/backend/electron/electronConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { join } from "path";
import fs from "fs";
import { app } from "electron";
import { moveFile } from "move-file";
import { writeFileSafely } from "./fileHelper";
import { BaseConfigManager, Metadata } from "@/backend/common/ConfigManager";
import { ConfigType } from "@/type/preload";

Expand All @@ -23,16 +23,7 @@ export class ElectronConfigManager extends BaseConfigManager {
}

protected async save(config: ConfigType & Metadata) {
// ファイル書き込みに失敗したときに設定が消えないように、tempファイル書き込み後上書き移動する
const temp_path = `${this.configPath}.tmp`;
await fs.promises.writeFile(
temp_path,
JSON.stringify(config, undefined, 2),
);

await moveFile(temp_path, this.configPath, {
overwrite: true,
});
writeFileSafely(this.configPath, JSON.stringify(config, undefined, 2));
}

private get configPath(): string {
Expand Down
18 changes: 18 additions & 0 deletions src/backend/electron/fileHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import fs from "fs";
import { moveFileSync } from "move-file";

/**
* 書き込みに失敗したときにファイルが消えないように、
* tmpファイル書き込み後、保存先ファイルに上書きする
*/
export function writeFileSafely(
path: string,
data: string | NodeJS.ArrayBufferView,
) {
const tmpPath = `${path}.tmp`;
fs.writeFileSync(tmpPath, data);

moveFileSync(tmpPath, path, {
overwrite: true,
});
}
3 changes: 2 additions & 1 deletion src/backend/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { RuntimeInfoManager } from "./manager/RuntimeInfoManager";
import { registerIpcMainHandle, ipcMainSendProxy, IpcMainHandle } from "./ipc";
import { getConfigManager } from "./electronConfig";
import { EngineAndVvppController } from "./engineAndVvppController";
import { writeFileSafely } from "./fileHelper";
import { failure, success } from "@/type/result";
import { AssetTextFileNames } from "@/type/staticResources";
import {
Expand Down Expand Up @@ -744,7 +745,7 @@ registerIpcMainHandle<IpcMainHandle>({

WRITE_FILE: (_, { filePath, buffer }) => {
try {
fs.writeFileSync(
writeFileSafely(
filePath,
new DataView(buffer instanceof Uint8Array ? buffer.buffer : buffer),
);
Expand Down
4 changes: 2 additions & 2 deletions src/backend/electron/manager/RuntimeInfoManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
* ランタイム情報には起動しているエンジンのURLなどが含まれる。
*/

import fs from "fs";
import AsyncLock from "async-lock";
import log from "electron-log/main";
import type { AltPortInfos } from "@/store/type";
import { EngineId, EngineInfo } from "@/type/preload";
import { writeFileSafely } from "@/backend/electron/fileHelper";

/**
* ランタイム情報書き出しに必要なEngineInfo
Expand Down Expand Up @@ -99,7 +99,7 @@ export class RuntimeInfoManager {

// ファイル書き出し
try {
await fs.promises.writeFile(
writeFileSafely(
this.runtimeInfoPath,
JSON.stringify(runtimeInfoFormatFor3rdParty), // FIXME: zod化する
);
Expand Down

0 comments on commit cb58a10

Please sign in to comment.