From 80ac882346b474bb7ea4e8c8afd4694bd13dd9dd Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 14 Oct 2026 10:17:49 +0200 Subject: feat(node): skip a file the disk stops answering for, then pause its root A Windows tester's scan stuck for good on one directory of a large disk: one read that never returned held the indexer's only thread, and with it every root behind that one. - A disk call is timed by its silence, not its length: each directory entry listed and each megabyte read counts as an answer. After 60 s of silence the call is abandoned on its daemon thread, the file is noted unread and the scan goes on. - Three silent files in a row pause the root (not persisted). Resume skips the files already known to give no answer; a restart, a replug or a change to the file retries them. - The walk is per directory with os.scandir: a subdirectory that cannot be listed costs itself, and a listing that stops answering pauses the root at once. A scan no longer stats anything from the event loop. - The log names the file after 10 s of silence, the indexing band shows it, and Group settings shows the paused root, where it stuck and the unread files with their reason, on the node's machine only. - generate_http_api.py writes LF on Windows too. Verified with real kernel-blocked opens (an unacknowledged oplock) in the installed frozen app. Co-Authored-By: Claude Opus 5.5 --- .../meshbay-hub/src/meshbay_hub/static/index-dock-model.js | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js b/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js index ed81e7c..f0f9aa0 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/index-dock-model.js @@ -35,9 +35,18 @@ export function fromLoopback(g) { filesDone: g.files_done || 0, filesTotal: g.files_total || 0, queued: Array.isArray(g.queued) ? g.queued : [], + // What the disk has given no answer on, and for how long. A path, so the + // loopback answer only: a push carries counters and nothing else. + slowPath: g.slow_path || '', + slowSecs: g.slow_secs || 0, }; } +/** The last segment of a path, whichever way its separators lean. */ +export function baseName(path) { + return String(path || '').split(/[\\/]/).filter(Boolean).pop() || ''; +} + export function fromPush(groupId, s, roots) { const pos = Number.isInteger(s.root_pos) ? s.root_pos : -1; const known = pos >= 0 && Array.isArray(roots) && roots[pos]; @@ -52,6 +61,8 @@ export function fromPush(groupId, s, roots) { filesDone: s.files_done || 0, filesTotal: s.files_total || 0, queued: Number.isInteger(s.queued) ? s.queued : 0, + slowPath: '', + slowSecs: 0, }; } -- cgit v1.2.3