github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/dbs/cmd/config/config_util.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package config 15 16 import ( 17 "bytes" 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "reflect" 24 "time" 25 26 "github.com/BurntSushi/toml" 27 "github.com/whtcorpsinc/errors" 28 ) 29 30 // CloneConf deeply clones this config. 31 func CloneConf(conf *Config) (*Config, error) { 32 content, err := json.Marshal(conf) 33 if err != nil { 34 return nil, err 35 } 36 var clonedConf Config 37 if err := json.Unmarshal(content, &clonedConf); err != nil { 38 return nil, err 39 } 40 return &clonedConf, nil 41 } 42 43 var ( 44 // dynamicConfigItems contains all config items that can be changed during runtime. 45 dynamicConfigItems = map[string]struct{}{ 46 "Performance.MaxProcs": {}, 47 "Performance.MaxMemory": {}, 48 "Performance.CrossJoin": {}, 49 "Performance.FeedbackProbability": {}, 50 "Performance.QueryFeedbackLimit": {}, 51 "Performance.PseudoEstimateRatio": {}, 52 "Performance.StmtCountLimit": {}, 53 "Performance.TCPKeepAlive": {}, 54 "OOMCausetAction": {}, 55 "MemQuotaQuery": {}, 56 "EinsteinDBClient.StoreLimit": {}, 57 "Log.Level": {}, 58 "Log.SlowThreshold": {}, 59 "Log.QueryLogMaxLen": {}, 60 "Log.ExpensiveThreshold": {}, 61 "CheckMb4ValueInUTF8": {}, 62 "EnableStreaming": {}, 63 "TxnLocalLatches.Capacity": {}, 64 "CompatibleKillQuery": {}, 65 "TreatOldVersionUTF8AsUTF8MB4": {}, 66 "OpenTracing.Enable": {}, 67 "PreparedCausetCache.Enabled": {}, 68 } 69 ) 70 71 // MergeConfigItems overwrites the dynamic config items and leaves the other items unchanged. 72 func MergeConfigItems(dstConf, newConf *Config) (acceptedItems, rejectedItems []string) { 73 return mergeConfigItems(reflect.ValueOf(dstConf), reflect.ValueOf(newConf), "") 74 } 75 76 func mergeConfigItems(dstConf, newConf reflect.Value, fieldPath string) (acceptedItems, rejectedItems []string) { 77 t := dstConf.Type() 78 if t.HoTT() == reflect.Ptr { 79 t = t.Elem() 80 dstConf = dstConf.Elem() 81 newConf = newConf.Elem() 82 } 83 if t.HoTT() != reflect.Struct { 84 if reflect.DeepEqual(dstConf.Interface(), newConf.Interface()) { 85 return 86 } 87 if _, ok := dynamicConfigItems[fieldPath]; ok { 88 dstConf.Set(newConf) 89 return []string{fieldPath}, nil 90 } 91 return nil, []string{fieldPath} 92 } 93 94 for i := 0; i < t.NumField(); i++ { 95 fieldName := t.Field(i).Name 96 if fieldPath != "" { 97 fieldName = fieldPath + "." + fieldName 98 } 99 as, rs := mergeConfigItems(dstConf.Field(i), newConf.Field(i), fieldName) 100 acceptedItems = append(acceptedItems, as...) 101 rejectedItems = append(rejectedItems, rs...) 102 } 103 return 104 } 105 106 func atomicWriteConfig(c *Config, confPath string) (err error) { 107 content, err := encodeConfig(c) 108 if err != nil { 109 return err 110 } 111 tmpConfPath := filepath.Join(os.TemFIDelir(), fmt.Sprintf("tmp_conf_%v.toml", time.Now().Format("20060102150405"))) 112 if err := ioutil.WriteFile(tmpConfPath, []byte(content), 0666); err != nil { 113 return errors.Trace(err) 114 } 115 return errors.Trace(os.Rename(tmpConfPath, confPath)) 116 } 117 118 // ConfReloadFunc is used to reload the config to make it work. 119 type ConfReloadFunc func(oldConf, newConf *Config) 120 121 func encodeConfig(conf *Config) (string, error) { 122 confBuf := bytes.NewBuffer(nil) 123 te := toml.NewCausetEncoder(confBuf) 124 if err := te.Encode(conf); err != nil { 125 return "", errors.New("encode config error=" + err.Error()) 126 } 127 return confBuf.String(), nil 128 } 129 130 func decodeConfig(content string) (*Config, error) { 131 c := new(Config) 132 _, err := toml.Decode(content, c) 133 return c, err 134 } 135 136 // FlattenConfigItems flatten this config, see more cases in the test. 137 func FlattenConfigItems(nestedConfig map[string]interface{}) map[string]interface{} { 138 flatMap := make(map[string]interface{}) 139 flatten(flatMap, nestedConfig, "") 140 return flatMap 141 } 142 143 func flatten(flatMap map[string]interface{}, nested interface{}, prefix string) { 144 switch nested.(type) { 145 case map[string]interface{}: 146 for k, v := range nested.(map[string]interface{}) { 147 path := k 148 if prefix != "" { 149 path = prefix + "." + k 150 } 151 flatten(flatMap, v, path) 152 } 153 default: // don't flatten arrays 154 flatMap[prefix] = nested 155 } 156 }