fix(proxy): use mtime-based config watching
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
cbde021d0c
commit
356eb9cec1
3 changed files with 56 additions and 27 deletions
|
|
@ -48,6 +48,10 @@ func TestConfigWatcher_Start_Good(t *testing.T) {
|
||||||
if err := os.WriteFile(path, updated, 0o644); err != nil {
|
if err := os.WriteFile(path, updated, 0o644); err != nil {
|
||||||
t.Fatalf("write updated config file: %v", err)
|
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 {
|
select {
|
||||||
case cfg := <-updates:
|
case cfg := <-updates:
|
||||||
|
|
@ -61,3 +65,42 @@ func TestConfigWatcher_Start_Good(t *testing.T) {
|
||||||
t.Fatal("expected watcher to reload updated config")
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
33
core_impl.go
33
core_impl.go
|
|
@ -364,9 +364,6 @@ func NewConfigWatcher(configPath string, onChange func(*Config)) *ConfigWatcher
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
done: make(chan struct{}),
|
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 {
|
if info, err := os.Stat(configPath); err == nil {
|
||||||
watcher.lastMod = info.ModTime()
|
watcher.lastMod = info.ModTime()
|
||||||
}
|
}
|
||||||
|
|
@ -392,28 +389,20 @@ func (w *ConfigWatcher) Start() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
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 {
|
if info, err := os.Stat(w.path); err == nil {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
w.lastMod = info.ModTime()
|
changed := info.ModTime() != w.lastMod
|
||||||
|
if changed {
|
||||||
|
w.lastMod = info.ModTime()
|
||||||
|
}
|
||||||
w.mu.Unlock()
|
w.mu.Unlock()
|
||||||
}
|
if !changed {
|
||||||
config, result := LoadConfig(w.path)
|
continue
|
||||||
if result.OK && config != nil {
|
}
|
||||||
w.onChange(config)
|
config, result := LoadConfig(w.path)
|
||||||
|
if result.OK && config != nil {
|
||||||
|
w.onChange(config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case <-w.done:
|
case <-w.done:
|
||||||
return
|
return
|
||||||
|
|
|
||||||
7
proxy.go
7
proxy.go
|
|
@ -110,16 +110,13 @@ type CloseEvent struct {
|
||||||
Miner *Miner
|
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) {
|
// watcher := proxy.NewConfigWatcher("config.json", func(cfg *proxy.Config) { p.Reload(cfg) })
|
||||||
// p.Reload(cfg)
|
|
||||||
// })
|
|
||||||
type ConfigWatcher struct {
|
type ConfigWatcher struct {
|
||||||
path string
|
path string
|
||||||
onChange func(*Config)
|
onChange func(*Config)
|
||||||
lastMod time.Time
|
lastMod time.Time
|
||||||
lastSum [32]byte
|
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
started bool
|
started bool
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue