From 56c0c7726cd01c7ee83a8ab005709e950e6851b4 Mon Sep 17 00:00:00 2001 From: William Stein Date: Fri, 17 Nov 2023 20:26:13 +0000 Subject: [PATCH] make watching more robust --- pnpm-lock.yaml | 64 ++++++++++++++++++++++++++++---- websocketfs/lib/metadata-file.ts | 38 ++++++++++--------- websocketfs/package.json | 3 +- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 229c56c..bca8b10 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,9 @@ importers: binarysearch: specifier: ^1.0.1 version: 1.0.1 + chokidar: + specifier: ^3.5.3 + version: 3.5.3 cookie: specifier: ^0.5.0 version: 0.5.0 @@ -984,7 +987,6 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true /aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} @@ -1087,6 +1089,11 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: false + /binarysearch@1.0.1: resolution: {integrity: sha512-FqhwdeXh1ZSAS/YpJ6lD9+SMf8JodCibe7c51Z9L1zAjHKUDTBisQgdmpfaL+m1qHvwAHnSLR8d9UHc79Hr34g==} dev: false @@ -1120,7 +1127,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browserslist@4.21.10: resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==} @@ -1208,6 +1214,21 @@ packages: engines: {node: '>=10'} dev: true + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: false + /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -1483,7 +1504,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} @@ -1526,7 +1546,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind@1.1.1: @@ -1570,6 +1589,13 @@ packages: /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: false + /glob@10.3.10: resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} engines: {node: '>=16 || 14 >=14.17'} @@ -1711,12 +1737,24 @@ packages: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: false + /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: has: 1.0.3 dev: true + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: false + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -1726,6 +1764,13 @@ packages: engines: {node: '>=6'} dev: true + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: false + /is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: false @@ -1733,7 +1778,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} @@ -2535,7 +2579,6 @@ packages: /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} @@ -2641,7 +2684,6 @@ packages: /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} @@ -2739,6 +2781,13 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: false + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3051,7 +3100,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} diff --git a/websocketfs/lib/metadata-file.ts b/websocketfs/lib/metadata-file.ts index 21f783b..896ca6a 100644 --- a/websocketfs/lib/metadata-file.ts +++ b/websocketfs/lib/metadata-file.ts @@ -2,18 +2,18 @@ import debug from "debug"; import { stat, readFile } from "fs/promises"; import binarySearch from "binarysearch"; import { symbolicToMode, readFileLz4 } from "./util"; +import { watch } from "chokidar"; +import { delay } from "awaiting"; const log = debug("websocketfs:metadata-file"); const log_cache = debug("cache"); -const METADATA_FILE_INTERVAL_MS = 3000; - export class MetadataFile { private attrCache; private dirCache; private metadataFile: string; private metadataFileContents: string[]; - private metadataFileInterval?: ReturnType; + private watcher?; private lastMtimeMs: number = 0; private state: "init" | "ready" | "expired" | "closed" = "init"; @@ -28,14 +28,19 @@ export class MetadataFile { return this.state == "ready"; }; - private init = () => { - if (this.metadataFileInterval) { - throw Error("bug -- do not call init more than once"); + private init = async () => { + // We keep trying util we succesfully + // read the file once. + while (!(await this.update())) { + await delay(3000); } - this.metadataFileInterval = setInterval( - this.update, - METADATA_FILE_INTERVAL_MS, - ); + // Only after reading the file, do we setup the watcher. + // Why? Because we might want to put the file itself inside + // of the websocketfs filesystem, and if we try to watch too + // soon then it doesn't work. Subtle. + this.watcher = watch(this.metadataFile); + this.watcher.on("change", this.update); + this.watcher.on("add", this.update); this.state = "expired"; this.update(); }; @@ -45,9 +50,9 @@ export class MetadataFile { try { const { mtimeMs } = await stat(this.metadataFile); if (mtimeMs <= this.lastMtimeMs) { - log("metadataFile:", this.metadataFile, "watching for changes"); + log("metadataFile:", this.metadataFile, "- no change"); // it hasn't changed so nothing to do - return; + return false; } const start = Date.now(); this.lastMtimeMs = mtimeMs; @@ -69,25 +74,24 @@ export class MetadataFile { Date.now() - start, "ms", ); + return true; } catch (err) { log( "metadataFile:", this.metadataFile, " - non-fatal issue reading - ", err.code == "ENOENT" - ? `no file '${this.metadataFile}' -- NOT updating metadata file cache (will try again soon)` + ? `no file '${this.metadataFile}' -- NOT updating` : err, ); + return false; } }; close = () => { this.state = "closed"; this.metadataFileContents = []; - if (this.metadataFileInterval) { - clearInterval(this.metadataFileInterval); - delete this.metadataFileInterval; - } + this.watcher?.close(); }; private find = (path: string): number => { diff --git a/websocketfs/package.json b/websocketfs/package.json index 76ffd78..de0f5f8 100644 --- a/websocketfs/package.json +++ b/websocketfs/package.json @@ -1,6 +1,6 @@ { "name": "websocketfs", - "version": "0.13.1", + "version": "0.14.1", "description": "Like sshfs, but over a WebSocket", "main": "./dist/lib/index.js", "scripts": { @@ -45,6 +45,7 @@ "@isaacs/ttlcache": "^1.4.1", "awaiting": "^3.0.0", "binarysearch": "^1.0.1", + "chokidar": "^3.5.3", "cookie": "^0.5.0", "debug": "^4.3.4", "lz4": "^0.6.5",