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  }