1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Add error resiliency to waitForPath

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-01-27 16:12:28 -05:00
parent 45907e8a2c
commit bc24aaa5ac
2 changed files with 67 additions and 30 deletions

View File

@ -3,30 +3,53 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { watch } from "chokidar";
import { FSWatcher } from "chokidar";
import path from "path";
/**
* Wait for `filePath` and all parent directories to exist.
* @param filePath The file path to wait until it exists
* @param pathname The file path to wait until it exists
*
* NOTE: There is technically a race condition in this function of the form
* "time-of-check to time-of-use" because we have to wait for each parent
* directory to exist first.
*/
export async function waitForPath(filePath: string): Promise<void> {
const dirOfPath = path.dirname(filePath);
export async function waitForPath(pathname: string): Promise<void> {
const dirOfPath = path.dirname(pathname);
if (dirOfPath === filePath) {
if (dirOfPath === pathname) {
// The root of this filesystem, assume it exists
return;
} else {
await waitForPath(dirOfPath);
}
return new Promise(resolve => {
watch(dirOfPath, {
return new Promise((resolve, reject) => {
const watcher = new FSWatcher({
depth: 0,
}).on("all", (event, path) => {
if ((event === "add" || event === "addDir") && path === filePath) {
resolve();
}
disableGlobbing: true,
});
const onAddOrAddDir = (filePath: string) => {
if (filePath === pathname) {
watcher.unwatch(dirOfPath);
watcher
.close()
.then(() => resolve())
.catch(reject);
}
};
const onError = (error: any) => {
watcher.unwatch(dirOfPath);
watcher
.close()
.then(() => reject(error))
.catch(() => reject(error));
};
watcher
.on("add", onAddOrAddDir)
.on("addDir", onAddOrAddDir)
.on("error", onError)
.add(dirOfPath);
});
}

View File

@ -6,7 +6,7 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import { computed, IComputedValue, observable } from "mobx";
import path from "path";
import os from "os";
import { getOrInsert, waitForPath } from "../../../utils";
import { delay, getOrInsert, waitForPath } from "../../../utils";
import { watch } from "chokidar";
import { readFile } from "fs/promises";
import logger from "../../../../common/logger";
@ -58,24 +58,38 @@ function watchUserCreateResourceTemplates(): IComputedValue<RawTemplates[]> {
templates.delete(filePath);
};
waitForPath(userTemplatesFolder)
.then(() => {
console.log("watching", userTemplatesFolder);
watch(userTemplatesFolder, {
disableGlobbing: true,
ignorePermissionErrors: true,
usePolling: false,
awaitWriteFinish: {
pollInterval: 100,
stabilityThreshold: 1000,
},
ignoreInitial: false,
atomic: 150, // for "atomic writes"
})
.on("add", onAddOrChange)
.on("change", onAddOrChange)
.on("unlink", onUnlink);
});
(async () => {
for (let i = 1;; i *= 2) {
try {
await waitForPath(userTemplatesFolder);
break;
} catch (error) {
logger.warn(`[USER-CREATE-RESOURCE-TEMPLATES]: encountered error while waiting for ${userTemplatesFolder} to exist, waiting and trying again`, error);
await delay(i * 1000); // exponential backoff in seconds
}
}
/**
* NOTE: There is technically a race condition here of the form "time-of-check to time-of-use"
*/
watch(userTemplatesFolder, {
disableGlobbing: true,
ignorePermissionErrors: true,
usePolling: false,
awaitWriteFinish: {
pollInterval: 100,
stabilityThreshold: 1000,
},
ignoreInitial: false,
atomic: 150, // for "atomic writes"
})
.on("add", onAddOrChange)
.on("change", onAddOrChange)
.on("unlink", onUnlink)
.on("error", error => {
logger.warn(`[USER-CREATE-RESOURCE-TEMPLATES]: encountered error while watching files under ${userTemplatesFolder}`, error);
});
})();
return computed(() => groupTemplates(templates));
}