Skip to content

Stop the file watcher from silently truncating its change list - #67

Merged
Seungpyo1007 merged 1 commit into
developfrom
fix/watcher-overflow
Aug 5, 2026
Merged

Stop the file watcher from silently truncating its change list#67
Seungpyo1007 merged 1 commit into
developfrom
fix/watcher-overflow

Conversation

@Seungpyo1007

@Seungpyo1007 Seungpyo1007 commented Aug 5, 2026

Copy link
Copy Markdown
Member

The bug

The file watcher collected changed paths with one line:

if (touched.size < MAX_TOUCHED) touched.add(...)   // MAX_TOUCHED = 2000

Past the cap it dropped paths silently. The app itself is fine — syncFromDisk re-reads the whole tree anyway. The victim is the extension file watcher: a dropped path becomes "nothing happened" for that extension, which then believes its index is complete.

The fix

electron/touchSet.cjs owns the bag now. The cap is 20000, and when it is exceeded the notification carries overflow: true. On overflow the renderer stops narrowing by the reported names and diffs the full before/after tree instead, so a dropped name cannot silently vanish. Only when the tree is also truncated do we genuinely not know, and that case is logged rather than passed off as complete.

What the probe disproved

This was built on the assumption that a branch switch overruns the 2000 cap. It does not. Creating 2500 files at once in the real app produced watcher notifications carrying ~180 names, not 2500 — fs.watch itself drops events long before our cap is reached (the Windows recursive watcher's kernel buffer overflows).

So the honest scope of this change is narrower than the framing suggests: it guarantees the app never truncates the list itself. Whether every name arrives is decided by the OS.

The app already survives that. syncFromDisk re-reads the tree and classify recovers created/deleted from the full diff — the probe confirms all 2500 files land in the tree. What is lost is changed events, and closing that properly needs content hashing or a polling fallback. Left as separate work.

Verification

  • 9 unit tests: overflow reporting, dropped count, duplicates not consuming the cap, backslash normalization, flag cleared on drain. 981 passing (was 972).
  • Real-app probe, 3/3: nothing truncated on our side (overflow=false), every reported path is a real path, all 2500 files present in the tree.
  • npm run typecheck clean. Line endings unchanged — main.cjs 1905 CRLF / 0 LF, preload.cjs 253 LF / 0 CRLF.

`if (touched.size < MAX_TOUCHED) touched.add(...)` 한 줄이었다. 상한(2000)을
넘으면 경로를 조용히 버렸다. 앱 자신은 멀쩡하다 — 새로고침 때 트리를 통째로
다시 읽으니까. 손해는 확장의 파일 감시자가 본다. 잘려 나간 파일은 확장에게
"아무 일 없었다" 가 되고, 확장은 자기가 전부 안다고 여긴다.

touchSet.cjs 로 자루를 옮긴다. 상한을 20000 으로 올리고, 그래도 넘치면
overflow 를 알림에 실어 보낸다. 렌더러는 그 신호를 받으면 이름 목록으로
좁히지 않고 앞뒤 트리 전체를 비교해 판정한다 — 잘려 나간 파일이 조용히
누락되지 않게. 트리까지 잘린 경우에만 정말 모르는 채 넘어가고 그때는 남긴다.

전제가 반쯤 틀렸다는 것도 적어 둔다. 파일 2500개를 한 번에 만들어 확인해
보니 워처 알림에 이름이 180개 남짓만 왔다. 우리 상한에 닿기 한참 전에
fs.watch 자체가 이벤트를 흘린다(윈도우 recursive 워처의 커널 버퍼). 실제
손실의 대부분은 거기서 일어난다. 그래서 이 수정이 보장하는 것은 "우리가
조용히 자르지 않는다" 까지고, "이름이 다 온다" 는 OS 가 정한다.

다행히 앱은 이미 견딘다. syncFromDisk 가 트리를 다시 읽고 classify 가 전체
비교로 만들어짐·지워짐을 잡는다. 놓치는 것은 changed 뿐이고, 그걸 메우려면
내용 해시나 폴링 대체 경로가 필요하다 — 별건으로 남긴다.

단위 9개 추가(981개 통과). 실제 앱 검증 3/3.
@Seungpyo1007 Seungpyo1007 added this to the v0.3 (2026-09-30) milestone Aug 5, 2026
@Seungpyo1007 Seungpyo1007 added the bug Something isn't working label Aug 5, 2026
@Seungpyo1007 Seungpyo1007 self-assigned this Aug 5, 2026
@Seungpyo1007
Seungpyo1007 merged commit dd67221 into develop Aug 5, 2026
1 check passed
@Seungpyo1007
Seungpyo1007 deleted the fix/watcher-overflow branch August 5, 2026 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant