Fix watcher recovery after atomic saves (#909)
* fix(watcher): delay rewatch after atomic saves * fix(watcher): retry watcher.Add with exponential backoff on atomic saves A single Add attempt after a fixed 100ms delay could fail if the file is momentarily absent (rename-away before recreate on slow or Docker bind-mount filesystems), leaving the file permanently unwatched. Retry up to 5 times with doubling delays (100ms → 1600ms); log only on final failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+18
-3
@@ -273,6 +273,23 @@ func (e *Engine) cacheFileChecksums(root string) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (e *Engine) rewatchFile(name string) {
|
||||
delay := time.Millisecond * 100
|
||||
maxRetries := 5
|
||||
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
time.Sleep(delay)
|
||||
err := e.watcher.Add(name)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
delay *= 2
|
||||
}
|
||||
e.watcherLog("failed to rewatch %s after %d retries", name, maxRetries)
|
||||
|
||||
}
|
||||
|
||||
func (e *Engine) watchPath(path string) error {
|
||||
if err := e.watcher.Add(path); err != nil {
|
||||
e.watcherLog("failed to watch %s, error: %s", path, err.Error())
|
||||
@@ -323,9 +340,7 @@ func (e *Engine) watchPath(path string) error {
|
||||
// Rewatch the file if the editor is using atomic saving.
|
||||
if renameOrRemoveEvent(ev) {
|
||||
if e.checkIncludeFile(ev.Name) {
|
||||
if err := e.watcher.Add(ev.Name); err != nil {
|
||||
e.watcherLog("error rewatching file: %v", err)
|
||||
}
|
||||
go e.rewatchFile(ev.Name)
|
||||
}
|
||||
}
|
||||
e.watcherDebug("%s has changed", e.config.rel(ev.Name))
|
||||
|
||||
Reference in New Issue
Block a user