-
Notifications
You must be signed in to change notification settings - Fork 26
/
config.go
121 lines (104 loc) · 2.84 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2023 PingCAP, Inc.
// SPDX-License-Identifier: Apache-2.0
package config
import (
"bytes"
"hash/crc32"
"os"
"time"
"github.com/BurntSushi/toml"
"github.com/fsnotify/fsnotify"
"github.com/pingcap/TiProxy/lib/config"
"go.uber.org/zap"
)
func (e *ConfigManager) reloadConfigFile(file string) error {
proxyConfigData, err := os.ReadFile(file)
if err != nil {
return err
}
return e.SetTOMLConfig(proxyConfigData)
}
func (e *ConfigManager) handleFSEvent(ev fsnotify.Event, f string) {
switch {
case ev.Has(fsnotify.Create), ev.Has(fsnotify.Write), ev.Has(fsnotify.Remove), ev.Has(fsnotify.Rename):
// The file may be the log file, triggering reload will cause more logs and thus cause reload again,
// so we need to filter the wrong files.
// The filesystem differs from OS to OS, so don't use string comparison.
f1, err := os.Stat(ev.Name)
if err != nil {
break
}
f2, err := os.Stat(f)
if err != nil {
break
}
if !os.SameFile(f1, f2) {
break
}
if ev.Has(fsnotify.Remove) || ev.Has(fsnotify.Rename) {
// in case of remove/rename the file, files are not present at filesystem for a while
// it may be too fast to read the config file now, sleep for a while
time.Sleep(50 * time.Millisecond)
}
// try to reload it
e.logger.Info("config file reloaded", zap.Stringer("event", ev), zap.Error(e.reloadConfigFile(f)))
}
}
// SetTOMLConfig will do partial config update. Usually, user will expect config changes
// only when they specified a config item. It is, however, impossible to tell a struct
// `c.max-conns == 0` means no user-input, or it specified `0`.
// So we always update the current config with a TOML string, which only overwrite fields
// that are specified by users.
func (e *ConfigManager) SetTOMLConfig(data []byte) (err error) {
e.sts.Lock()
defer func() {
if err == nil {
e.logger.Info("current config", zap.Any("cfg", e.sts.current))
}
e.sts.Unlock()
}()
base := e.sts.current
if base == nil {
base = config.NewConfig()
} else {
base = base.Clone()
}
if err = toml.Unmarshal(data, base); err != nil {
return
}
if err = toml.Unmarshal(e.overlay, base); err != nil {
return
}
if err = base.Check(); err != nil {
return
}
e.sts.current = base
var buf bytes.Buffer
if err = toml.NewEncoder(&buf).Encode(base); err != nil {
return
}
e.sts.checksum = crc32.ChecksumIEEE(buf.Bytes())
for _, list := range e.sts.listeners {
list <- base.Clone()
}
return
}
func (e *ConfigManager) GetConfig() *config.Config {
e.sts.Lock()
v := e.sts.current
e.sts.Unlock()
return v
}
func (e *ConfigManager) GetConfigChecksum() uint32 {
e.sts.Lock()
c := e.sts.checksum
e.sts.Unlock()
return c
}
func (e *ConfigManager) WatchConfig() <-chan *config.Config {
ch := make(chan *config.Config)
e.sts.Lock()
e.sts.listeners = append(e.sts.listeners, ch)
e.sts.Unlock()
return ch
}