Preflight Checklist
- [x] I have searched the issue tracker for an issue that matches the one I want to file, without success.
- [x] I am not looking for support or already pursued the available support channels without success.
- [x] I have checked the troubleshooting guide for my problem, without success.
Viper Version
1.20.1
Go Version
1.24.5
Config Source
Files
Format
JSON
Repl.it link
No response
Code reproducing the issue
Expected Behavior
A method should be available to manually stop WatchConfig().
Actual Behavior
There are no publicly available methods to stop WatchConfig().
Currently, it seems that memory is only released by the GC when both of the following conditions are met:
-
The Goroutine has exited:
- The configuration file is deleted (triggering an
fsnotify.Remove
event). - Or an error occurs in the watcher (the
watcher.Errors
channel is closed or returns an error). - At this point, the goroutine executes
eventsWG.Done()
and exits, releasing its reference tov
.
- The configuration file is deleted (triggering an
-
No other external references exist:
- Apart from this goroutine, no other code holds a reference to
v
(e.g., the caller has setv
tonil
or no longer uses it).
- Apart from this goroutine, no other code holds a reference to
Steps To Reproduce
No response
Additional Information
Many times, it's necessary to dynamically switch between different configuration files for corresponding settings. While a UI is provided to trigger this (requiring SSE pushes via WatchConfig), the process of switching cannot completely clear the resource usage of the old WatchConfig, leading to leakage issues.
Comment From: LuSrackhall
Fix Recommendations (Preventing Memory Leaks)
To proactively release memory, add an explicit stop mechanism (e.g., include a stop channel in the Viper
struct):
```go type Viper struct { // Add a stop channel stopCh chan struct{} }
func (v *Viper) WatchConfig() { // ...existing code... go func() { defer eventsWG.Done() for { select { case <-v.stopCh: // Listen for stop signal return case event, ok := <-watcher.Events: // ...handle events... case err, ok := <-watcher.Errors: // ...handle errors... } } }() }
// Add a stop method func (v *Viper) StopWatch() { close(v.stopCh) // Close the channel to trigger goroutine exit }
Comment From: LuSrackhall
If possible, I would be happy to submit a PR for the related issue.