Files
tasks/common/dynamicconfig/shared_structure.go
David ReissandGitHub 083dbb13dc Warn on dynamic config default values with shared structure (#8176)
## What changed?
Log softassert warnings if dynamic config settings are registered with
default values with shared structure.

## Why?
This is very likely unintended and may lead to unexpected behavior of
settings (values will be parsed on top of a copy of the default).

## How did you test it?
- [x] run locally and tested manually
- [x] added new unit test(s)
2025-08-18 12:41:41 -07:00

59 lines
1.4 KiB
Go

package dynamicconfig
import (
"fmt"
"reflect"
"sync"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/softassert"
)
var (
// These should all run at static init time, but use synchronization just in case.
sharedStructureWarnings sync.Map
logSharedStructureWarningsOnce sync.Once
)
func warnDefaultSharedStructure(key Key, def any) {
if path := hasSharedStructure(reflect.ValueOf(def), "root"); path != "" {
sharedStructureWarnings.Store(key, path)
}
}
func logSharedStructureWarnings(logger log.Logger) {
logSharedStructureWarningsOnce.Do(func() {
sharedStructureWarnings.Range(func(key, path any) bool {
softassert.Fail(logger, fmt.Sprintf("default value for %v contains shared structure at %v", key, path))
return true
})
})
}
func hasSharedStructure(v reflect.Value, path string) string {
// nolint:exhaustive // deliberately not exhaustive
switch v.Kind() {
case reflect.Map, reflect.Slice, reflect.Pointer:
if !v.IsNil() {
return path
}
case reflect.Interface:
if !v.IsNil() {
return hasSharedStructure(v.Elem(), path)
}
case reflect.Struct:
for i := range v.NumField() {
if p := hasSharedStructure(v.Field(i), path+"."+v.Type().Field(i).Name); p != "" {
return p
}
}
case reflect.Array:
for i := range v.Len() {
if p := hasSharedStructure(v.Index(i), path+"."+fmt.Sprintf("[%d]", i)); p != "" {
return p
}
}
}
return ""
}