Skip to content

Prevent file and goroutine leaks during self-hosted log tailing - #828

Merged
seanlinsley merged 3 commits into
mainfrom
log-tail-cancel-fixes
Jul 9, 2026
Merged

Prevent file and goroutine leaks during self-hosted log tailing#828
seanlinsley merged 3 commits into
mainfrom
log-tail-cancel-fixes

Conversation

@seanlinsley

Copy link
Copy Markdown
Member

No description provided.

out <- SelfHostedLogStreamItem{Line: line.String()}
case line, ok := <-t.Lines():
if !ok {
break TailLoop

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Lines channel could close due to an internal error. In that case we should break out of the loop to free the file descriptor

}
select {
case out <- SelfHostedLogStreamItem{Line: line.String()}:
case <-ctx.Done():

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for context cancellation before writing to the out channel to avoid a possible deadlock

@lfittl lfittl Jul 8, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense especially since that channel is currently unbuffered (and it seems beneficial to keep that, or at most use a low capacity, so that we backpressure on the actual file read in go-tail to avoid high memory use with large log lines).

err = watcher.Add(logLocation)
if err != nil {
watcher.Close()
return fmt.Errorf("fsnotify add \"%s\": %s", logLocation, err)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

watcher.Add is now before the goroutine launch so Add failures don't orphan the watcher goroutine and its open file tails.

}
}
if event.Op&fsnotify.Remove == fsnotify.Remove || event.Op&fsnotify.Rename == fsnotify.Rename || event.Op&fsnotify.Chmod == fsnotify.Chmod {
if event.Op&fsnotify.Remove == fsnotify.Remove || event.Op&fsnotify.Rename == fsnotify.Rename {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to the rest of the changes, but I'm not sure it makes sense to close the tail if the permissions change. At the very least we can end up losing in-progress logs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable.

@seanlinsley
seanlinsley marked this pull request as ready for review July 8, 2026 21:32
Comment on lines 81 to 86
func (t *Follower) Close() {
if t.file != nil {
t.file.Close()
}
t.closeCh <- struct{}{}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this change is not actually safe to do, since it causes a data race between Go routines (since we're calling t.file.Close() from a different Go routine than the one that actually manages the file), as observed in https://github.com/pganalyze/collector/actions/runs/29125918855/job/86472202020?pr=827

If I recall our conversation correctly, this eager closing was actually not strictly required, since making the channel buffered addresses the Go routine getting stuck. I've dug a bit into this, and revised that change further in pganalyze/go-tail#1 and also made a PR to update to that: #836

@seanlinsley seanlinsley changed the title Prevent file descriptor and goroutine leaks in log tailing Prevent file and goroutine leaks during self-hosted log tailing Jul 9, 2026
@seanlinsley
seanlinsley merged commit da9c28a into main Jul 9, 2026
5 checks passed
@seanlinsley
seanlinsley deleted the log-tail-cancel-fixes branch July 9, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants