fix(proxy): use mtime-based config watching

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-05 02:04:03 +00:00
parent cbde021d0c
commit 356eb9cec1
3 changed files with 56 additions and 27 deletions

View file

@ -48,6 +48,10 @@ func TestConfigWatcher_Start_Good(t *testing.T) {
if err := os.WriteFile(path, updated, 0o644); err != nil {
t.Fatalf("write updated config file: %v", err)
}
now := time.Now()
if err := os.Chtimes(path, now, now.Add(2*time.Second)); err != nil {
t.Fatalf("touch updated config file: %v", err)
}
select {
case cfg := <-updates:
@ -61,3 +65,42 @@ func TestConfigWatcher_Start_Good(t *testing.T) {
t.Fatal("expected watcher to reload updated config")
}
}
func TestConfigWatcher_Start_Ugly(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "config.json")
initial := []byte(`{"mode":"nicehash","workers":"false","bind":[{"host":"127.0.0.1","port":3333}],"pools":[{"url":"pool.example:3333","enabled":true}]}`)
if err := os.WriteFile(path, initial, 0o644); err != nil {
t.Fatalf("write initial config file: %v", err)
}
updates := make(chan *Config, 1)
watcher := NewConfigWatcher(path, func(cfg *Config) {
select {
case updates <- cfg:
default:
}
})
if watcher == nil {
t.Fatal("expected watcher")
}
watcher.Start()
defer watcher.Stop()
now := time.Now()
if err := os.Chtimes(path, now, now.Add(2*time.Second)); err != nil {
t.Fatalf("touch config file: %v", err)
}
select {
case cfg := <-updates:
if cfg == nil {
t.Fatal("expected config update")
}
if got := cfg.Mode; got != "nicehash" {
t.Fatalf("expected unchanged mode, got %q", got)
}
case <-time.After(5 * time.Second):
t.Fatal("expected watcher to reload touched config")
}
}

View file

@ -364,9 +364,6 @@ func NewConfigWatcher(configPath string, onChange func(*Config)) *ConfigWatcher
onChange: onChange,
done: make(chan struct{}),
}
if data, err := os.ReadFile(configPath); err == nil {
watcher.lastSum = sha256.Sum256(data)
}
if info, err := os.Stat(configPath); err == nil {
watcher.lastMod = info.ModTime()
}
@ -392,28 +389,20 @@ func (w *ConfigWatcher) Start() {
for {
select {
case <-ticker.C:
data, err := os.ReadFile(w.path)
if err != nil {
continue
}
sum := sha256.Sum256(data)
w.mu.Lock()
changed := sum != w.lastSum
if changed {
w.lastSum = sum
}
w.mu.Unlock()
if !changed {
continue
}
if info, err := os.Stat(w.path); err == nil {
w.mu.Lock()
w.lastMod = info.ModTime()
changed := info.ModTime() != w.lastMod
if changed {
w.lastMod = info.ModTime()
}
w.mu.Unlock()
}
config, result := LoadConfig(w.path)
if result.OK && config != nil {
w.onChange(config)
if !changed {
continue
}
config, result := LoadConfig(w.path)
if result.OK && config != nil {
w.onChange(config)
}
}
case <-w.done:
return

View file

@ -110,16 +110,13 @@ type CloseEvent struct {
Miner *Miner
}
// ConfigWatcher polls a config file for changes.
// ConfigWatcher polls a config file every second and reloads on modification.
//
// watcher := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) {
// p.Reload(cfg)
// })
// watcher := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) { p.Reload(cfg) })
type ConfigWatcher struct {
path string
onChange func(*Config)
lastMod time.Time
lastSum [32]byte
done chan struct{}
mu sync.Mutex
started bool