github.com/matrixorigin/matrixone@v1.2.0/pkg/util/dump_config.go (about) 1 // Copyright 2022 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "fmt" 19 "github.com/matrixorigin/matrixone/pkg/common/moerr" 20 logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" 21 "reflect" 22 "strings" 23 "sync/atomic" 24 ) 25 26 type item struct { 27 v string 28 userSet string 29 } 30 31 func (i *item) value() string { 32 return i.v 33 } 34 35 func (i *item) internal() string { 36 if len(i.userSet) != 0 { 37 return i.userSet 38 } else { 39 return "internal" 40 } 41 } 42 43 type exporter struct { 44 kvs map[string]item 45 } 46 47 func newExporter() *exporter { 48 return &exporter{ 49 kvs: make(map[string]item), 50 } 51 } 52 53 func (et *exporter) Export(k, v, userSet string) { 54 k = strings.ToLower(k) 55 et.kvs[k] = item{ 56 v: v, 57 userSet: userSet, 58 } 59 } 60 61 func (et *exporter) Print() { 62 //for k, v := range et.kvs { 63 // fmt.Println(k, v.v, v.userSet) 64 //} 65 } 66 67 func (et *exporter) Dump() map[string]item { 68 return et.kvs 69 } 70 71 func (et *exporter) Clear() { 72 et.kvs = make(map[string]item) 73 } 74 75 // flatten is a recursive function to flatten a struct. 76 // in: the struct object to be flattened. reference or pointer 77 // name: the name of the struct object. empty if it is the top level struct. 78 // prefix: the path to the name. empty if it is the top level struct. 79 // userSet: the user_setting tag of the field. empty if it is not set in definition. 80 // exp: the exporter to export the key-value pairs. 81 func flatten(in any, name string, prefix string, userSet string, exp *exporter) error { 82 if exp == nil { 83 return moerr.NewInternalErrorNoCtx("invalid exporter") 84 } 85 var err error 86 if in == nil { 87 //fmt.Printf("%s + %v\n", prefix+name, "nil") 88 exp.Export(prefix+name, "nil", userSet) 89 return nil 90 } 91 92 typ := reflect.TypeOf(in) 93 value := reflect.ValueOf(in) 94 95 oldPrefix := prefix 96 if name != "" { 97 prefix = prefix + name 98 } else { 99 prefix = prefix + typ.Name() 100 } 101 102 switch typ.Kind() { 103 case reflect.Invalid: 104 return moerr.NewInternalErrorNoCtx("unsupported type %v", typ.Kind()) 105 case reflect.Bool: 106 fallthrough 107 case reflect.Int: 108 fallthrough 109 case reflect.Int8: 110 fallthrough 111 case reflect.Int16: 112 fallthrough 113 case reflect.Int32: 114 fallthrough 115 case reflect.Int64: 116 fallthrough 117 case reflect.Uint: 118 fallthrough 119 case reflect.Uint8: 120 fallthrough 121 case reflect.Uint16: 122 fallthrough 123 case reflect.Uint32: 124 fallthrough 125 case reflect.Uint64: 126 fallthrough 127 case reflect.Float32: 128 fallthrough 129 case reflect.Float64: 130 fallthrough 131 case reflect.Complex64: 132 fallthrough 133 case reflect.Complex128: 134 fallthrough 135 case reflect.String: 136 exp.Export(prefix, fmt.Sprintf("%v", value), userSet) 137 case reflect.Uintptr: 138 return moerr.NewInternalErrorNoCtx("unsupported type %v", typ.Kind()) 139 case reflect.Slice: 140 fallthrough 141 case reflect.Array: 142 if value.Len() == 0 { 143 exp.Export(prefix+typ.Name(), "", userSet) 144 } else { 145 for k := 0; k < value.Len(); k++ { 146 elemVal := value.Index(k) 147 err = flatten(elemVal.Interface(), typ.Name(), oldPrefix+name+"["+fmt.Sprintf("%d", k)+"].", userSet, exp) 148 if err != nil { 149 return err 150 } 151 } 152 } 153 154 case reflect.Chan: 155 return moerr.NewInternalErrorNoCtx("unsupported type %v", typ.Kind()) 156 case reflect.Func: 157 exp.Export(prefix+typ.Name(), "", userSet) 158 case reflect.Interface: 159 exp.Export(prefix+typ.Name(), "", userSet) 160 case reflect.Map: 161 if value.Len() == 0 { 162 exp.Export(prefix+typ.Name(), "", userSet) 163 } else { 164 keys := value.MapKeys() 165 for _, key := range keys { 166 keyVal := value.MapIndex(key) 167 err = flatten(keyVal.Interface(), typ.Name(), oldPrefix+name+"<"+fmt.Sprintf("%v", key.Interface())+">.", userSet, exp) 168 if err != nil { 169 return err 170 } 171 } 172 } 173 174 case reflect.Pointer: 175 if value.IsNil() { 176 exp.Export(prefix+typ.Name(), "nil", userSet) 177 } else { 178 nextPrefix := "" 179 if oldPrefix == "" { 180 nextPrefix = oldPrefix 181 } else { 182 nextPrefix = prefix 183 } 184 err = flatten(value.Elem().Interface(), typ.Name(), nextPrefix+".", userSet, exp) 185 if err != nil { 186 return err 187 } 188 } 189 case reflect.Struct: 190 for i := 0; i < typ.NumField(); i++ { 191 field := typ.Field(i) 192 isExported := field.IsExported() 193 var fieldVal reflect.Value 194 if isExported { 195 fieldVal = value.Field(i) 196 } else { 197 continue 198 } 199 200 tagValue := userSet 201 if v, ok := field.Tag.Lookup("user_setting"); ok { 202 tagValue = v 203 } 204 205 err = flatten(fieldVal.Interface(), field.Name, prefix+".", tagValue, exp) 206 if err != nil { 207 return err 208 } 209 } 210 case reflect.UnsafePointer: 211 return moerr.NewInternalErrorNoCtx("unsupported type %v", typ.Kind()) 212 default: 213 return moerr.NewInternalErrorNoCtx("unsupported type %v", typ.Kind()) 214 } 215 return err 216 } 217 218 func DumpConfig(cfg any, defCfg any) (map[string]*logservicepb.ConfigItem, error) { 219 ret := make(map[string]*logservicepb.ConfigItem) 220 if cfg == nil || defCfg == nil { 221 return ret, moerr.NewInvalidInputNoCtx("invalid input cfg or defCfg") 222 } 223 224 //dump current 225 curExp := newExporter() 226 err := flatten(cfg, "", "", "", curExp) 227 if err != nil { 228 return nil, err 229 } 230 231 //dump default 232 defExp := newExporter() 233 err = flatten(defCfg, "", "", "", defExp) 234 if err != nil { 235 return nil, err 236 } 237 238 //make new map 239 for name, value := range curExp.Dump() { 240 citem := &logservicepb.ConfigItem{ 241 Name: name, 242 CurrentValue: value.value(), 243 Internal: value.internal(), 244 } 245 246 //set default value 247 if v, ok := defExp.Dump()[name]; ok { 248 citem.DefaultValue = v.value() 249 } 250 251 ret[name] = citem 252 } 253 return ret, err 254 } 255 256 // MergeConfig copy all items from src to dst and overwrite the existed item. 257 func MergeConfig(dst *ConfigData, src map[string]*logservicepb.ConfigItem) { 258 if dst == nil || dst.configData == nil || src == nil { 259 return 260 } 261 for s, item := range src { 262 dst.configData[s] = item 263 } 264 } 265 266 const ( 267 count = 50 268 ) 269 270 type ConfigData struct { 271 count atomic.Int32 272 configData map[string]*logservicepb.ConfigItem 273 } 274 275 func NewConfigData(data map[string]*logservicepb.ConfigItem) *ConfigData { 276 ret := &ConfigData{ 277 count: atomic.Int32{}, 278 configData: make(map[string]*logservicepb.ConfigItem, len(data)), 279 } 280 for k, v := range data { 281 ret.configData[k] = v 282 } 283 ret.count.Store(count) 284 return ret 285 } 286 287 func (cd *ConfigData) GetData() *logservicepb.ConfigData { 288 if cd.count.Load() > 0 { 289 return &logservicepb.ConfigData{ 290 Content: cd.configData, 291 } 292 } 293 return nil 294 } 295 296 func (cd *ConfigData) DecrCount() { 297 if cd.count.Load() > 0 { 298 cd.count.Add(-1) 299 } 300 }