diff --git a/cmd/tools/gendynamicconfig/dynamic_config.tmpl b/cmd/tools/gendynamicconfig/dynamic_config.tmpl index 38f5bb0166..6eeab03d8b 100644 --- a/cmd/tools/gendynamicconfig/dynamic_config.tmpl +++ b/cmd/tools/gendynamicconfig/dynamic_config.tmpl @@ -22,10 +22,11 @@ const ( {{- range $P := $Precedences}} {{ if $T.IsGeneric -}} type {{$P.Name}}TypedSetting[T any] setting[T, func({{$P.GoArgs}})] +type {{$P.Name}}TypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func({{$P.GoArgs}})] // New{{$P.Name}}TypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func New{{$P.Name}}TypedSetting[T any](key Key, def T, description string) {{$P.Name}}TypedSetting[T] { s := {{$P.Name}}TypedSetting[T]{ key: key, @@ -50,10 +51,10 @@ func New{{$P.Name}}TypedSettingWithConverter[T any](key Key, convert func(any) ( } // New{{$P.Name}}TypedSettingWithConstrainedDefault creates a setting with a compound default value. -func New{{$P.Name}}TypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) {{$P.Name}}TypedSetting[T] { - s := {{$P.Name}}TypedSetting[T]{ +func New{{$P.Name}}TypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) {{$P.Name}}TypedConstrainedDefaultSetting[T] { + s := {{$P.Name}}TypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -68,6 +69,13 @@ func (s {{$P.Name}}TypedSetting[T]) Validate(v any) error { return err } +func (s {{$P.Name}}TypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s {{$P.Name}}TypedConstrainedDefaultSetting[T]) Precedence() Precedence { return Precedence{{$P.Name}} } +func (s {{$P.Name}}TypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s {{$P.Name}}TypedSetting[T]) WithDefault(v T) {{$P.Name}}TypedSetting[T] { newS := s newS.def = v @@ -92,6 +100,22 @@ func (s {{$P.Name}}TypedSetting[T]) Get(c *Collection) TypedPropertyFnWith{{$P.N c, s.key, s.def, + s.convert, + prec, + ) + } +} + +{{if eq $P.Name "Global" -}} +func (s {{$P.Name}}TypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFn[T] { +{{- else -}} +func (s {{$P.Name}}TypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWith{{$P.Name}}Filter[T] { +{{- end}} + return func({{$P.GoArgs}}) T { + prec := {{$P.Expr}} + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -113,7 +137,7 @@ func (s {{$P.Name}}TypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWi return func({{$P.GoArgs}}, callback func(T)) (T, func()) { {{- end}} prec := {{$P.Expr}} - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -127,6 +151,10 @@ func (s {{$P.Name}}TypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs [ ) } +func (s {{$P.Name}}TypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + {{if eq $P.Name "Global" -}} func GetTypedPropertyFn[T any](value T) TypedPropertyFn[T] { {{- else -}} @@ -138,12 +166,13 @@ func GetTypedPropertyFnFilteredBy{{$P.Name}}[T any](value T) TypedPropertyFnWith } {{else -}} type {{$P.Name}}{{$T.Name}}Setting = {{$P.Name}}TypedSetting[{{$T.GoType}}] +type {{$P.Name}}{{$T.Name}}ConstrainedDefaultSetting = {{$P.Name}}TypedConstrainedDefaultSetting[{{$T.GoType}}] func New{{$P.Name}}{{$T.Name}}Setting(key Key, def {{$T.GoType}}, description string) {{$P.Name}}{{$T.Name}}Setting { return New{{$P.Name}}TypedSettingWithConverter[{{$T.GoType}}](key, convert{{$T.Name}}, def, description) } -func New{{$P.Name}}{{$T.Name}}SettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[{{$T.GoType}}], description string) {{$P.Name}}{{$T.Name}}Setting { +func New{{$P.Name}}{{$T.Name}}SettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[{{$T.GoType}}], description string) {{$P.Name}}{{$T.Name}}ConstrainedDefaultSetting { return New{{$P.Name}}TypedSettingWithConstrainedDefault[{{$T.GoType}}](key, convert{{$T.Name}}, cdef, description) } @@ -164,4 +193,4 @@ func Get{{$T.Name}}PropertyFnFilteredBy{{$P.Name}}(value {{$T.GoType}}) {{$T.Nam {{- end}} {{end }}{{/* if $T.IsGeneric */}} {{- end}}{{/* range $T :=.Types */}} -{{- end}}{{/* range $P := $Precedences */}} \ No newline at end of file +{{- end}}{{/* range $P := $Precedences */}} diff --git a/common/dynamicconfig/cachedvalue.go b/common/dynamicconfig/cachedvalue.go deleted file mode 100644 index d6e95a6b43..0000000000 --- a/common/dynamicconfig/cachedvalue.go +++ /dev/null @@ -1,58 +0,0 @@ -package dynamicconfig - -import ( - "fmt" - "sync/atomic" - - "go.temporal.io/server/common/log/tag" -) - -// GlobalCachedTypedValue holds a cached value of a GlobalTypedSetting after applying a supplied convert function. -// This type should be deleted after dynamicconfig handles conversion more efficiently. -type GlobalCachedTypedValue[T any] struct { - ptr atomic.Pointer[T] - cancel func() -} - -// NewGlobalCachedTypedValue returns a new [GlobalCachedTypedValue] for the given setting, applying the given convert -// function on change. -// If convert returns an error the old value will be preserved. If convert never succeeds, Get() will return the zero -// value of T. -func NewGlobalCachedTypedValue[T, I any](c *Collection, setting GlobalTypedSetting[I], convert func(I) (T, error)) *GlobalCachedTypedValue[T] { - cachedValue := new(GlobalCachedTypedValue[T]) - initial, cancel := setting.Subscribe(c)(func(i I) { - converted, err := convert(i) - if err != nil { - c.logger.Warn(fmt.Sprintf("Failed to convert %q attribute", setting.Key().String()), tag.Value(i), tag.Error(err)) - return - } - // Make sure to only store the value on successful conversion. - cachedValue.ptr.Store(&converted) - }) - cachedValue.cancel = cancel - - converted, err := convert(initial) - if err != nil { - c.logger.Warn(fmt.Sprintf("Failed to convert %q attribute", setting.Key().String()), tag.Value(initial), tag.Error(err)) - } else { - // Make sure to only store the value on successful conversion. - cachedValue.ptr.Store(&converted) - } - - return cachedValue -} - -// Get the last successfully converted cached value or the zero value if convert has never succeeded. -func (s *GlobalCachedTypedValue[T]) Get() T { - v := s.ptr.Load() - if v == nil { - var t T - return t - } - return *v -} - -func (s *GlobalCachedTypedValue[T]) Close() error { - s.cancel() - return nil -} diff --git a/common/dynamicconfig/client.go b/common/dynamicconfig/client.go index aec1ef668d..298d1eac80 100644 --- a/common/dynamicconfig/client.go +++ b/common/dynamicconfig/client.go @@ -23,6 +23,11 @@ type ( // Note that GetValue is called very often! You should not synchronously call out to an // external system. Instead you should keep a set of all configured values, refresh it // periodically or when notified, and only do in-memory lookups inside of GetValue. + // + // Implementations should prefer to return the same slice in response to the same key + // as long as the value hasn't changed. Value conversions are cached using weak + // pointers into the returned slice, so new slices will result in unnecessary calls to + // conversion functions. GetValue(key Key) []ConstrainedValue } diff --git a/common/dynamicconfig/collection.go b/common/dynamicconfig/collection.go index 4cb8f52a70..957b5322a3 100644 --- a/common/dynamicconfig/collection.go +++ b/common/dynamicconfig/collection.go @@ -5,11 +5,13 @@ import ( "errors" "fmt" "reflect" + "runtime" "strconv" "strings" "sync" "sync/atomic" "time" + "weak" "github.com/mitchellh/mapstructure" "go.temporal.io/server/common/clock" @@ -39,6 +41,11 @@ type ( callbackPool *goro.AdaptivePool poller goro.Group + + // cache converted values. use weak pointers to avoid holding on to values in the cache + // that are no longer in use. + convertCacheLock sync.Mutex + convertCache map[weak.Pointer[ConstrainedValue]]any } subscription[T any] struct { @@ -46,9 +53,8 @@ type ( prec []Constraints f func(T) def T - cdef *[]TypedConstrainedValue[T] // protected by subscriptionLock in Collection: - prev T + raw any // raw value that last sent value was converted from } subscriptionCallbackSettings struct { @@ -58,6 +64,9 @@ type ( ShrinkFactor float64 } + // sentinel type that doesn't compare equal to anything else + defaultValue struct{} + // These function types follow a similar pattern: // {X}PropertyFn - returns a value of type X that is global (no filters) // {X}PropertyFnWith{Y}Filter - returns a value of type X with the given filters @@ -88,6 +97,8 @@ var ( errorType = reflect.TypeOf((*error)(nil)).Elem() durationType = reflect.TypeOf(time.Duration(0)) stringType = reflect.TypeOf("") + + usingDefaultValue any = defaultValue{} ) // NewCollection creates a new collection. For subscriptions to work, you must call Start/Stop. @@ -98,6 +109,7 @@ func NewCollection(client Client, logger log.Logger) *Collection { logger: logger, errCount: -1, subscriptions: make(map[Key]map[int]any), + convertCache: make(map[weak.Pointer[ConstrainedValue]]any), } } @@ -202,19 +214,18 @@ func (c *Collection) throttleLog() bool { return errCount < errCountLogThreshold || errCount%errCountLogThreshold == 0 } -func findMatch[T any](cvs []ConstrainedValue, defaultCVs []TypedConstrainedValue[T], precedence []Constraints) (any, error) { - if len(cvs)+len(defaultCVs) == 0 { +func findMatch(cvs []ConstrainedValue, precedence []Constraints) (*ConstrainedValue, error) { + if len(cvs) == 0 { return nil, errKeyNotPresent } for _, m := range precedence { - for _, cv := range cvs { - if m == cv.Constraints { - return cv.Value, nil - } - } - for _, cv := range defaultCVs { + for idx, cv := range cvs { if m == cv.Constraints { - return cv.Value, nil + // Note: cvs here is the slice returned by Client.GetValue. We want to return a + // pointer into that slice so that the converted value is cached as long as the + // Client keeps the []ConstrainedValue alive. See the comment on + // Client.GetValue. + return &cvs[idx], nil } } } @@ -228,61 +239,120 @@ func matchAndConvert[T any]( c *Collection, key Key, def T, - cdef *[]TypedConstrainedValue[T], convert func(value any) (T, error), precedence []Constraints, ) T { cvs := c.client.GetValue(key) - return matchAndConvertCvs(c, key, def, cdef, convert, precedence, cvs) + v, _ := matchAndConvertCvs(c, key, def, convert, precedence, cvs) + return v } func matchAndConvertCvs[T any]( c *Collection, key Key, def T, - cdef *[]TypedConstrainedValue[T], convert func(value any) (T, error), precedence []Constraints, cvs []ConstrainedValue, -) T { - var defaultCVs []TypedConstrainedValue[T] - if cdef != nil { - defaultCVs = *cdef - } else { - defaultCVs = []TypedConstrainedValue[T]{{Value: def}} +) (T, any) { + cvp, err := findMatch(cvs, precedence) + if err != nil { + if c.throttleLog() { + c.logger.Debug("No such key in dynamic config, using default", tag.Key(key.String()), tag.Error(err)) + } + // couldn't find a constrained match, use default + return def, usingDefaultValue } - val, matchErr := findMatch(cvs, defaultCVs, precedence) - if matchErr != nil { + typedVal, err := convertWithCache(c, key, convert, cvp) + if err != nil { + // We failed to convert the value to the desired type. Use the default. if c.throttleLog() { - c.logger.Debug("No such key in dynamic config, using default", tag.Key(key.String()), tag.Error(matchErr)) + c.logger.Warn("Failed to convert value, using default", tag.Key(key.String()), tag.IgnoredValue(cvp), tag.Error(err)) + } + return def, usingDefaultValue + } + return typedVal, cvp.Value +} + +// Returns matched value out of cvs, matched default out of defaultCVs, and also the priorities +// of each of the matches (lower matched first). For no match, order will be 0. +func findMatchWithConstrainedDefaults[T any](cvs []ConstrainedValue, defaultCVs []TypedConstrainedValue[T], precedence []Constraints) ( + matchedValue *ConstrainedValue, + matchedDefault T, + valueOrder int, + defaultOrder int, +) { + order := 0 + for _, m := range precedence { + for idx, cv := range cvs { + order++ + if m == cv.Constraints { + if valueOrder == 0 { + valueOrder = order + // Note: cvs here is the slice returned by Client.GetValue. We want to + // return a pointer into that slice instead of copying the ConstrainedValue. + // See findMatch. + matchedValue = &cvs[idx] + } + } + } + for _, cv := range defaultCVs { + order++ + if m == cv.Constraints { + if defaultOrder == 0 { + defaultOrder = order + matchedDefault = cv.Value + } + } } - // couldn't find a constrained match, use default - val = def } + return +} - typedVal, convertErr := convert(val) - if convertErr != nil && matchErr == nil { - // We failed to convert the value to the desired type. Try converting the default. note - // that if matchErr != nil then val _is_ defaultValue and we don't have to try this again. +func matchAndConvertWithConstrainedDefault[T any]( + c *Collection, + key Key, + cdef []TypedConstrainedValue[T], + convert func(value any) (T, error), + precedence []Constraints, +) T { + cvs := c.client.GetValue(key) + cvp, defVal, valOrder, defOrder := findMatchWithConstrainedDefaults(cvs, cdef, precedence) + if defOrder == 0 { + // This is a server bug: all precedence lists must end with no-constraints, and all + // constrained defaults must have a no-constraints value, so we should have gotten a match. + c.logger.Warn("Constrained defaults had no match (this is a bug; fix server code)", tag.Key(key.String())) + // leave defVal as the zero value, that's the best we can do + } + if valOrder == 0 { if c.throttleLog() { - c.logger.Warn("Failed to convert value, using default", tag.Key(key.String()), tag.IgnoredValue(val), tag.Error(convertErr)) + c.logger.Debug("No such key in dynamic config, using default", tag.Key(key.String())) } - typedVal, convertErr = convert(def) + return defVal } - if convertErr != nil { - // If we can't convert the default, that's a bug in our code, use Warn level. - c.logger.Warn("Can't convert default value (this is a bug; fix server code)", tag.Key(key.String()), tag.IgnoredValue(def), tag.Error(convertErr)) - // Return typedVal anyway since we have to return something. + if defOrder < valOrder { + // value was present but constrained default took precedence + return defVal + } + typedVal, err := convertWithCache(c, key, convert, cvp) + if err != nil { + // We failed to convert the value to the desired type. Use the default. + if c.throttleLog() { + c.logger.Warn("Failed to convert value, using default", tag.Key(key.String()), tag.IgnoredValue(cvp), tag.Error(err)) + } + // if defOrder == 0, this will be the zero value, but that's the best we can do + return defVal } return typedVal } +// Note: subscriptions currently only work with regular (single default) settings, not +// constrained default settings. func subscribe[T any]( c *Collection, key Key, def T, - cdef *[]TypedConstrainedValue[T], convert func(value any) (T, error), prec []Constraints, callback func(T), @@ -292,7 +362,8 @@ func subscribe[T any]( // get one value immediately (note that subscriptionLock is held here so we can't race with // an update) - init := matchAndConvert(c, key, def, cdef, convert, prec) + cvs := c.client.GetValue(key) + init, raw := matchAndConvertCvs(c, key, def, convert, prec, cvs) // As a convenience (and for efficiency), you can pass in a nil callback; we just return the // current value and skip the subscription. The cancellation func returned is also nil. @@ -311,8 +382,7 @@ func subscribe[T any]( prec: prec, f: callback, def: def, - cdef: cdef, - prev: init, + raw: raw, } return init, func() { @@ -330,15 +400,78 @@ func dispatchUpdate[T any]( sub *subscription[T], cvs []ConstrainedValue, ) { - newVal := matchAndConvertCvs(c, key, sub.def, sub.cdef, convert, sub.prec, cvs) - // Unfortunately we have to use reflect.DeepEqual instead of just == because T is not comparable. - // We can't make T comparable because maps and slices are not comparable, and we want to support - // those directly. We could have two versions of this, one for comparable types and one for - // non-comparable, but it's not worth it. - if !reflect.DeepEqual(sub.prev, newVal) { - sub.prev = newVal - c.callbackPool.Do(func() { sub.f(newVal) }) + var raw any + cvp, err := findMatch(cvs, sub.prec) + if err != nil { + if c.throttleLog() { + c.logger.Debug("No such key in dynamic config, using default", tag.Key(key.String()), tag.Error(err)) + } + raw = usingDefaultValue + } else { + raw = cvp.Value + } + + // compare raw (pre-conversion) values, if unchanged, skip this update. note that + // `usingDefaultValue` is equal to itself but nothing else. + if reflect.DeepEqual(sub.raw, raw) { + // make raw field point to new one, not old one, so that old loaded files can get + // garbage collected. + sub.raw = raw + return } + + // raw value changed, need to dispatch default or converted value + var newVal T + if cvp == nil { + newVal = sub.def + } else { + newVal, err = convertWithCache(c, key, convert, cvp) + if err != nil { + // We failed to convert the value to the desired type. Use the default. + if c.throttleLog() { + c.logger.Warn("Failed to convert value, using default", tag.Key(key.String()), tag.IgnoredValue(cvp), tag.Error(err)) + } + newVal, raw = sub.def, usingDefaultValue + } + } + + sub.raw = raw + c.callbackPool.Do(func() { sub.f(newVal) }) +} + +func convertWithCache[T any](c *Collection, key Key, convert func(any) (T, error), cvp *ConstrainedValue) (T, error) { + weakcvp := weak.Make(cvp) + + c.convertCacheLock.Lock() + if converted, ok := c.convertCache[weakcvp]; ok { + if t, ok := converted.(T); ok { + c.convertCacheLock.Unlock() + return t, nil + } + // Each key can only be used with a single type, so this shouldn't happen + c.logger.Warn("Cached converted value has wrong type", tag.Key(key.String())) + // Fall through to regular conversion + } + c.convertCacheLock.Unlock() + + // unlock around convert + t, err := convert(cvp.Value) + if err != nil { + var zero T + return zero, err + } + + c.convertCacheLock.Lock() + c.convertCache[weakcvp] = t + c.convertCacheLock.Unlock() + + runtime.AddCleanup(cvp, func(w weak.Pointer[ConstrainedValue]) { + c.convertCacheLock.Lock() + delete(c.convertCache, w) + c.convertCacheLock.Unlock() + }, weakcvp) + + return t, nil } func convertInt(val any) (int, error) { @@ -434,22 +567,28 @@ func convertMap(val any) (map[string]any, error) { // Note that any failure in conversion of _any_ field will result in the overall default being used, // ignoring the fields that successfully converted. // -// Note that the default value will be shallow-copied, so it should not have any deep structure. -// Scalar types and values are fine, and slice and map types are fine too as long as they're set to -// nil in the default. +// Note that the default value will be deep-copied and then passed to mapstructure with the +// ZeroFields setting false, so the config value will be _merged_ on top of it. Be very careful +// when using non-empty maps or slices, the result may not be what you want. // // To avoid confusion, the default passed to ConvertStructure should be either the same as the // overall default for the setting (if you want any value set to be merged over the default, i.e. // treat the fields independently), or the zero value of its type (if you want to treat the fields // as a group and default unset fields to zero). func ConvertStructure[T any](def T) func(v any) (T, error) { + // call deepCopyForMapstructure once to surface any potential panic at static init time. + _ = deepCopyForMapstructure(def) + return func(v any) (T, error) { // if we already have the right type, no conversion is necessary if typedV, ok := v.(T); ok { return typedV, nil } - out := def + // Deep-copy the default and decode over it. This allows using e.g. a struct with some + // default fields filled in and a config that only set some fields. + out := deepCopyForMapstructure(def) + dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: &out, DecodeHook: mapstructure.ComposeDecodeHookFunc( diff --git a/common/dynamicconfig/collection_bench_test.go b/common/dynamicconfig/collection_bench_test.go index 9a1ea091b5..5812ded4d9 100644 --- a/common/dynamicconfig/collection_bench_test.go +++ b/common/dynamicconfig/collection_bench_test.go @@ -8,38 +8,80 @@ import ( ) func BenchmarkCollection(b *testing.B) { - // client with just one value client1 := dynamicconfig.StaticClient{ - dynamicconfig.MatchingMaxTaskBatchSize.Key(): []dynamicconfig.ConstrainedValue{{Value: 12}}, + dynamicconfig.MatchingMaxTaskBatchSize.Key(): []dynamicconfig.ConstrainedValue{{Value: 12}}, + dynamicconfig.HistoryRPS.Key(): []dynamicconfig.ConstrainedValue{{Value: 100}}, + dynamicconfig.BlobSizeLimitError.Key(): []dynamicconfig.ConstrainedValue{{Value: 100}}, + dynamicconfig.BlobSizeLimitWarn.Key(): []dynamicconfig.ConstrainedValue{{Value: 100}}, + dynamicconfig.MatchingShutdownDrainDuration.Key(): []dynamicconfig.ConstrainedValue{{Value: "100s"}}, } cln1 := dynamicconfig.NewCollection(client1, log.NewNoopLogger()) - b.Run("global int", func(b *testing.B) { + b.Run("global int default", func(b *testing.B) { b.ReportAllocs() + size := dynamicconfig.MatchingThrottledLogRPS.Get(cln1) for i := 0; i < b.N/2; i++ { - size := dynamicconfig.MatchingThrottledLogRPS.Get(cln1) _ = size() - size = dynamicconfig.MatchingThrottledLogRPS.Get(cln1) _ = size() } }) - b.Run("namespace int", func(b *testing.B) { + b.Run("global int present", func(b *testing.B) { b.ReportAllocs() + size := dynamicconfig.HistoryRPS.Get(cln1) for i := 0; i < b.N/2; i++ { - size := dynamicconfig.HistoryMaxPageSize.Get(cln1) - _ = size("my-namespace") - size = dynamicconfig.WorkflowExecutionMaxInFlightUpdates.Get(cln1) - _ = size("my-namespace") + _ = size() + _ = size() + } + }) + b.Run("namespace int default", func(b *testing.B) { + b.ReportAllocs() + size1 := dynamicconfig.HistoryMaxPageSize.Get(cln1) + size2 := dynamicconfig.WorkflowExecutionMaxInFlightUpdates.Get(cln1) + for i := 0; i < b.N/2; i++ { + _ = size1("my-namespace") + _ = size2("my-namespace") + } + }) + b.Run("namespace int present", func(b *testing.B) { + b.ReportAllocs() + size1 := dynamicconfig.BlobSizeLimitError.Get(cln1) + size2 := dynamicconfig.BlobSizeLimitWarn.Get(cln1) + for i := 0; i < b.N/2; i++ { + _ = size1("my-namespace") + _ = size2("my-namespace") + } + }) + b.Run("taskqueue int default", func(b *testing.B) { + b.ReportAllocs() + size := dynamicconfig.MatchingMaxTaskDeleteBatchSize.Get(cln1) + for i := 0; i < b.N/2; i++ { + _ = size("my-namespace", "my-task-queue", 1) + _ = size("my-namespace", "my-task-queue", 1) } }) - b.Run("taskqueue int", func(b *testing.B) { + b.Run("taskqueue int present", func(b *testing.B) { b.ReportAllocs() + size := dynamicconfig.MatchingMaxTaskBatchSize.Get(cln1) for i := 0; i < b.N/2; i++ { - size := dynamicconfig.MatchingMaxTaskBatchSize.Get(cln1) _ = size("my-namespace", "my-task-queue", 1) - size = dynamicconfig.MatchingGetTasksBatchSize.Get(cln1) _ = size("my-namespace", "my-task-queue", 1) } }) + b.Run("global duration default", func(b *testing.B) { + b.ReportAllocs() + size := dynamicconfig.MatchingAlignMembershipChange.Get(cln1) + for i := 0; i < b.N/2; i++ { + _ = size() + _ = size() + } + }) + b.Run("global duration present", func(b *testing.B) { + b.ReportAllocs() + size := dynamicconfig.MatchingShutdownDrainDuration.Get(cln1) + for i := 0; i < b.N/2; i++ { + _ = size() + _ = size() + } + }) // client with more constrained values client2 := dynamicconfig.StaticClient{ @@ -61,27 +103,21 @@ func BenchmarkCollection(b *testing.B) { cln2 := dynamicconfig.NewCollection(client2, log.NewNoopLogger()) b.Run("single default", func(b *testing.B) { b.ReportAllocs() + size := dynamicconfig.MatchingMaxTaskBatchSize.Get(cln2) for i := 0; i < b.N/4; i++ { - size := dynamicconfig.MatchingMaxTaskBatchSize.Get(cln2) _ = size("my-namespace", "my-task-queue", 1) - size = dynamicconfig.MatchingMaxTaskBatchSize.Get(cln2) _ = size("my-namespace", "other-tq", 1) - size = dynamicconfig.MatchingMaxTaskBatchSize.Get(cln2) _ = size("other-ns", "my-task-queue", 1) - size = dynamicconfig.MatchingMaxTaskBatchSize.Get(cln2) _ = size("other-ns", "other-tq", 1) } }) b.Run("structured default", func(b *testing.B) { b.ReportAllocs() + size := dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(cln2) for i := 0; i < b.N/4; i++ { - size := dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(cln2) _ = size("my-namespace", "my-task-queue", 1) - size = dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(cln2) _ = size("my-namespace", "other-tq", 1) - size = dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(cln2) _ = size("other-ns", "my-task-queue", 1) - size = dynamicconfig.MatchingNumTaskqueueWritePartitions.Get(cln2) _ = size("other-ns", "other-tq", 1) } }) diff --git a/common/dynamicconfig/constants.go b/common/dynamicconfig/constants.go index 675d5e57c0..fa3749ca81 100644 --- a/common/dynamicconfig/constants.go +++ b/common/dynamicconfig/constants.go @@ -550,11 +550,13 @@ is currently processing a task. ) // keys for frontend - FrontendHTTPAllowedHosts = NewGlobalTypedSetting( + FrontendHTTPAllowedHosts = NewGlobalTypedSettingWithConverter( "frontend.httpAllowedHosts", - []string(nil), + ConvertWildcardStringListToRegexp, + MatchAnythingRE, `HTTP API Requests with a "Host" header matching the allowed hosts will be processed, otherwise rejected. -Wildcards (*) are expanded to allow any substring. By default any Host header is allowed.`, +Wildcards (*) are expanded to allow any substring. By default any Host header is allowed. +Concrete type should be list of strings.`, ) FrontendPersistenceMaxQPS = NewGlobalIntSetting( "frontend.persistenceMaxQPS", @@ -891,11 +893,13 @@ used when the first cache layer has a miss. Requires server restart for change t `The TTL of the Nexus endpoint registry's readthrough LRU cache - the cache is a secondary cache and is only used when the first cache layer has a miss. Requires server restart for change to be applied.`, ) - FrontendNexusRequestHeadersBlacklist = NewGlobalTypedSetting( + FrontendNexusRequestHeadersBlacklist = NewGlobalTypedSettingWithConverter( "frontend.nexusRequestHeadersBlacklist", - []string(nil), + ConvertWildcardStringListToRegexp, + MatchNothingRE, `Nexus request headers to be removed before being sent to a user handler. -Wildcards (*) are expanded to allow any substring. By default blacklist is empty.`, +Wildcards (*) are expanded to allow any substring. By default blacklist is empty. +Concrete type should be list of strings.`, ) FrontendCallbackURLMaxLength = NewNamespaceIntSetting( "frontend.callbackURLMaxLength", diff --git a/common/dynamicconfig/deepcopy.go b/common/dynamicconfig/deepcopy.go new file mode 100644 index 0000000000..e5fa2a8fbb --- /dev/null +++ b/common/dynamicconfig/deepcopy.go @@ -0,0 +1,68 @@ +package dynamicconfig + +import ( + "fmt" + "reflect" +) + +// deepCopyForMapstructure does a simple deep copy of T. Fancy cases (anything other than plain old data) +// is not handled and will panic. +func deepCopyForMapstructure[T any](t T) T { + // nolint:revive // this will be triggered from a static initializer before it can be triggered from production code + return deepCopyValue(reflect.ValueOf(t)).Interface().(T) +} + +func deepCopyValue(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.String: + nv := reflect.New(v.Type()).Elem() + nv.Set(v) + return nv + case reflect.Array: + nv := reflect.New(v.Type()).Elem() + for i := range v.Len() { + nv.Index(i).Set(deepCopyValue(v.Index(i))) + } + return nv + case reflect.Map: + if v.IsNil() { + return v + } + nv := reflect.MakeMapWithSize(v.Type(), v.Len()) + for i := v.MapRange(); i.Next(); { + nv.SetMapIndex(i.Key(), deepCopyValue(i.Value())) + } + return nv + case reflect.Pointer: + if v.IsNil() { + return v + } + return deepCopyValue(v.Elem()).Addr() + case reflect.Slice: + if v.IsNil() { + return v + } + nv := reflect.MakeSlice(v.Type(), v.Len(), v.Len()) + for i := range v.Len() { + nv.Index(i).Set(deepCopyValue(v.Index(i))) + } + return nv + case reflect.Struct: + nv := reflect.New(v.Type()).Elem() + for i := range v.Type().NumField() { + nv.Field(i).Set(deepCopyValue(v.Field(i))) + } + return nv + case reflect.Interface, reflect.Func, reflect.Chan: + // only nil values of any other reference types allowed! + if v.IsNil() { + return v + } + fallthrough + default: + // nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code + panic(fmt.Sprintf("Can't deep copy value of type %s: %v", v.Type(), v)) + } +} diff --git a/common/dynamicconfig/deepcopy_test.go b/common/dynamicconfig/deepcopy_test.go new file mode 100644 index 0000000000..a612eafbb1 --- /dev/null +++ b/common/dynamicconfig/deepcopy_test.go @@ -0,0 +1,111 @@ +package dynamicconfig + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDeepCopy_IntSlice(t *testing.T) { + a := []int{1, 2, 3} + b := deepCopyForMapstructure(a) + a[1]++ + assert.NotEqual(t, a[1], b[1]) +} + +func TestDeepCopy_StringSlice(t *testing.T) { + a := []string{"one", "two", "three"} + b := deepCopyForMapstructure(a) + a[1] = "four" + assert.NotEqual(t, a[1], b[1]) +} + +func TestDeepCopy_SimpleStruct(t *testing.T) { + a := struct { + A, B int + C string + D [2]float32 + }{A: 4, B: 6, C: "eight", D: [2]float32{5.555, 6.666}} + b := deepCopyForMapstructure(a) + a.B++ + a.C = "ten" + a.D[0] *= 1.1 + assert.NotEqual(t, a.B, b.B) + assert.NotEqual(t, a.C, b.C) + assert.NotEqual(t, a.D, b.D) +} + +func TestDeepCopy_Pointer(t *testing.T) { + v := 10 + a := &v + b := deepCopyForMapstructure(a) + (*a)++ + assert.NotEqual(t, *a, *b) +} + +func TestDeepCopy_Pointers(t *testing.T) { + type L struct { + L *L + V *int + } + v := 10 + a := L{ + L: &L{ + L: &L{ + L: &L{ + V: &v, + }, + }, + }, + } + b := deepCopyForMapstructure(a) + (*a.L.L.L.V)++ + assert.NotEqual(t, *a.L.L.L.V, *b.L.L.L.V) +} + +func TestDeepCopy_Map(t *testing.T) { + a := map[int]int{3: 5} + b := deepCopyForMapstructure(a) + a[3] = 7 + a[8] = 9 + assert.Equal(t, b[3], 5) + assert.Zero(t, b[8]) +} + +func TestDeepCopy_MapMap(t *testing.T) { + a := map[int]map[string]int{ + 3: map[string]int{"three": 3}, + 5: map[string]int{"five": 5}, + } + b := deepCopyForMapstructure(a) + a[5]["five"] = 3 + assert.Equal(t, b[5]["five"], 5) +} + +func TestDeepCopy_OtherReferenceTypes_Nil(t *testing.T) { + a := struct { + I context.Context + F func(string) string + C chan error + }{} + assert.NotPanics(t, func() { + _ = deepCopyForMapstructure(a) + }) +} + +func TestDeepCopy_OtherReferenceTypes_NonNil(t *testing.T) { + assert.Panics(t, func() { + a := struct{ C context.Context }{C: context.Background()} + _ = deepCopyForMapstructure(a) + }) + assert.Panics(t, func() { + a := struct{ F func(string) string }{F: strings.ToLower} + _ = deepCopyForMapstructure(a) + }) + assert.Panics(t, func() { + a := struct{ C chan error }{C: make(chan error)} + _ = deepCopyForMapstructure(a) + }) +} diff --git a/common/dynamicconfig/file_based_client_test.go b/common/dynamicconfig/file_based_client_test.go index eccbc08d95..a3ae2b972c 100644 --- a/common/dynamicconfig/file_based_client_test.go +++ b/common/dynamicconfig/file_based_client_test.go @@ -11,6 +11,7 @@ import ( enumsspb "go.temporal.io/server/api/enums/v1" "go.temporal.io/server/common/dynamicconfig" "go.temporal.io/server/common/log" + "go.temporal.io/server/common/log/tag" "go.temporal.io/server/common/retrypolicy" "go.uber.org/mock/gomock" ) @@ -334,6 +335,7 @@ testGetBoolPropertyKey: reader.EXPECT().GetModTime().Return(originModTime, nil).Times(2) reader.EXPECT().ReadFile().Return(originFileData, nil) + mockLogger.EXPECT().Debug(gomock.Any(), tag.Key(dynamicconfig.DynamicConfigSubscriptionCallback.Key().String()), gomock.Any()).AnyTimes() mockLogger.EXPECT().Info(gomock.Any()).Times(6) client, err := dynamicconfig.NewFileBasedClientWithReader(reader, &dynamicconfig.FileBasedClientConfig{ diff --git a/common/dynamicconfig/find_match_test.go b/common/dynamicconfig/find_match_test.go index 74da5d8caf..85103067f8 100644 --- a/common/dynamicconfig/find_match_test.go +++ b/common/dynamicconfig/find_match_test.go @@ -54,57 +54,100 @@ func TestFindMatch(t *testing.T) { } for _, tc := range testCases { - _, err := findMatch[struct{}](tc.v, nil, tc.filters) + _, err := findMatch(tc.v, tc.filters) assert.Equal(t, tc.matched, err == nil) } } func TestFindMatchWithTyped(t *testing.T) { testCases := []struct { - tv []TypedConstrainedValue[struct{}] - filters []Constraints - matched bool + val []ConstrainedValue + tv []TypedConstrainedValue[struct{}] + filters []Constraints + valOrder int + defOrder int }{ { + val: nil, tv: []TypedConstrainedValue[struct{}]{ {Constraints: Constraints{}}, }, filters: []Constraints{ {Namespace: "some random namespace"}, }, - matched: false, + valOrder: 0, + defOrder: 0, }, { + val: nil, tv: []TypedConstrainedValue[struct{}]{ {Constraints: Constraints{Namespace: "samples-namespace"}}, }, filters: []Constraints{ {Namespace: "some random namespace"}, }, - matched: false, + valOrder: 0, + defOrder: 0, }, { + val: nil, tv: []TypedConstrainedValue[struct{}]{ {Constraints: Constraints{Namespace: "samples-namespace", TaskQueueName: "sample-task-queue"}}, }, filters: []Constraints{ {Namespace: "samples-namespace", TaskQueueName: "sample-task-queue"}, }, - matched: true, + valOrder: 0, + defOrder: 1, }, { + val: nil, tv: []TypedConstrainedValue[struct{}]{ {Constraints: Constraints{Namespace: "samples-namespace"}}, }, filters: []Constraints{ {TaskQueueName: "sample-task-queue"}, }, - matched: false, + valOrder: 0, + defOrder: 0, + }, + { + val: []ConstrainedValue{ + {Constraints: Constraints{Namespace: "ns"}}, + }, + tv: []TypedConstrainedValue[struct{}]{ + {Constraints: Constraints{Namespace: "ns", TaskQueueName: "othertq"}}, + {}, + }, + filters: []Constraints{ + {Namespace: "ns", TaskQueueName: "tq"}, + {Namespace: "ns"}, + {}, + }, + valOrder: 4, + defOrder: 9, + }, + { + val: []ConstrainedValue{ + {Constraints: Constraints{Namespace: "ns"}}, + }, + tv: []TypedConstrainedValue[struct{}]{ + {Constraints: Constraints{Namespace: "ns", TaskQueueName: "tq"}}, + {}, + }, + filters: []Constraints{ + {Namespace: "ns", TaskQueueName: "tq"}, + {Namespace: "ns"}, + {}, + }, + valOrder: 4, + defOrder: 2, }, } for _, tc := range testCases { - _, err := findMatch(nil, tc.tv, tc.filters) - assert.Equal(t, tc.matched, err == nil) + _, _, valOrder, defOrder := findMatchWithConstrainedDefaults(tc.val, tc.tv, tc.filters) + assert.Equal(t, tc.valOrder, valOrder) + assert.Equal(t, tc.defOrder, defOrder) } } diff --git a/common/dynamicconfig/setting.go b/common/dynamicconfig/setting.go index 37cdeb2610..00dc9f54b5 100644 --- a/common/dynamicconfig/setting.go +++ b/common/dynamicconfig/setting.go @@ -14,13 +14,19 @@ type ( // T is the data type of the setting. P is a go type representing the precedence, which is // just used to make the types more unique. setting[T any, P any] struct { - key Key // string value of key. case-insensitive. - def T // default value. cdef is used in preference to def if non-nil. - cdef *[]TypedConstrainedValue[T] + key Key // string value of key. case-insensitive. + def T // default value convert func(any) (T, error) // converter function description string // documentation } + constrainedDefaultSetting[T any, P any] struct { + key Key // string value of key. case-insensitive. + cdef []TypedConstrainedValue[T] // default values + convert func(any) (T, error) // converter function + description string // documentation + } + // GenericSetting is an interface that all instances of Setting implement (by generated // code in setting_gen.go). It can be used to refer to settings of any type and deal with // them generically.. diff --git a/common/dynamicconfig/setting_gen.go b/common/dynamicconfig/setting_gen.go index de9da8e8a1..b68afae5e5 100644 --- a/common/dynamicconfig/setting_gen.go +++ b/common/dynamicconfig/setting_gen.go @@ -22,12 +22,13 @@ const ( ) type GlobalBoolSetting = GlobalTypedSetting[bool] +type GlobalBoolConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[bool] func NewGlobalBoolSetting(key Key, def bool, description string) GlobalBoolSetting { return NewGlobalTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewGlobalBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) GlobalBoolSetting { +func NewGlobalBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) GlobalBoolConstrainedDefaultSetting { return NewGlobalTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -38,12 +39,13 @@ func GetBoolPropertyFn(value bool) BoolPropertyFn { } type NamespaceBoolSetting = NamespaceTypedSetting[bool] +type NamespaceBoolConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[bool] func NewNamespaceBoolSetting(key Key, def bool, description string) NamespaceBoolSetting { return NewNamespaceTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewNamespaceBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) NamespaceBoolSetting { +func NewNamespaceBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) NamespaceBoolConstrainedDefaultSetting { return NewNamespaceTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -54,12 +56,13 @@ func GetBoolPropertyFnFilteredByNamespace(value bool) BoolPropertyFnWithNamespac } type NamespaceIDBoolSetting = NamespaceIDTypedSetting[bool] +type NamespaceIDBoolConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[bool] func NewNamespaceIDBoolSetting(key Key, def bool, description string) NamespaceIDBoolSetting { return NewNamespaceIDTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewNamespaceIDBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) NamespaceIDBoolSetting { +func NewNamespaceIDBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) NamespaceIDBoolConstrainedDefaultSetting { return NewNamespaceIDTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -70,12 +73,13 @@ func GetBoolPropertyFnFilteredByNamespaceID(value bool) BoolPropertyFnWithNamesp } type TaskQueueBoolSetting = TaskQueueTypedSetting[bool] +type TaskQueueBoolConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[bool] func NewTaskQueueBoolSetting(key Key, def bool, description string) TaskQueueBoolSetting { return NewTaskQueueTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewTaskQueueBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) TaskQueueBoolSetting { +func NewTaskQueueBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) TaskQueueBoolConstrainedDefaultSetting { return NewTaskQueueTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -86,12 +90,13 @@ func GetBoolPropertyFnFilteredByTaskQueue(value bool) BoolPropertyFnWithTaskQueu } type ShardIDBoolSetting = ShardIDTypedSetting[bool] +type ShardIDBoolConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[bool] func NewShardIDBoolSetting(key Key, def bool, description string) ShardIDBoolSetting { return NewShardIDTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewShardIDBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) ShardIDBoolSetting { +func NewShardIDBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) ShardIDBoolConstrainedDefaultSetting { return NewShardIDTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -102,12 +107,13 @@ func GetBoolPropertyFnFilteredByShardID(value bool) BoolPropertyFnWithShardIDFil } type TaskTypeBoolSetting = TaskTypeTypedSetting[bool] +type TaskTypeBoolConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[bool] func NewTaskTypeBoolSetting(key Key, def bool, description string) TaskTypeBoolSetting { return NewTaskTypeTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewTaskTypeBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) TaskTypeBoolSetting { +func NewTaskTypeBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) TaskTypeBoolConstrainedDefaultSetting { return NewTaskTypeTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -118,12 +124,13 @@ func GetBoolPropertyFnFilteredByTaskType(value bool) BoolPropertyFnWithTaskTypeF } type DestinationBoolSetting = DestinationTypedSetting[bool] +type DestinationBoolConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[bool] func NewDestinationBoolSetting(key Key, def bool, description string) DestinationBoolSetting { return NewDestinationTypedSettingWithConverter[bool](key, convertBool, def, description) } -func NewDestinationBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) DestinationBoolSetting { +func NewDestinationBoolSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[bool], description string) DestinationBoolConstrainedDefaultSetting { return NewDestinationTypedSettingWithConstrainedDefault[bool](key, convertBool, cdef, description) } @@ -134,12 +141,13 @@ func GetBoolPropertyFnFilteredByDestination(value bool) BoolPropertyFnWithDestin } type GlobalIntSetting = GlobalTypedSetting[int] +type GlobalIntConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[int] func NewGlobalIntSetting(key Key, def int, description string) GlobalIntSetting { return NewGlobalTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewGlobalIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) GlobalIntSetting { +func NewGlobalIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) GlobalIntConstrainedDefaultSetting { return NewGlobalTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -150,12 +158,13 @@ func GetIntPropertyFn(value int) IntPropertyFn { } type NamespaceIntSetting = NamespaceTypedSetting[int] +type NamespaceIntConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[int] func NewNamespaceIntSetting(key Key, def int, description string) NamespaceIntSetting { return NewNamespaceTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewNamespaceIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) NamespaceIntSetting { +func NewNamespaceIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) NamespaceIntConstrainedDefaultSetting { return NewNamespaceTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -166,12 +175,13 @@ func GetIntPropertyFnFilteredByNamespace(value int) IntPropertyFnWithNamespaceFi } type NamespaceIDIntSetting = NamespaceIDTypedSetting[int] +type NamespaceIDIntConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[int] func NewNamespaceIDIntSetting(key Key, def int, description string) NamespaceIDIntSetting { return NewNamespaceIDTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewNamespaceIDIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) NamespaceIDIntSetting { +func NewNamespaceIDIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) NamespaceIDIntConstrainedDefaultSetting { return NewNamespaceIDTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -182,12 +192,13 @@ func GetIntPropertyFnFilteredByNamespaceID(value int) IntPropertyFnWithNamespace } type TaskQueueIntSetting = TaskQueueTypedSetting[int] +type TaskQueueIntConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[int] func NewTaskQueueIntSetting(key Key, def int, description string) TaskQueueIntSetting { return NewTaskQueueTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewTaskQueueIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) TaskQueueIntSetting { +func NewTaskQueueIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) TaskQueueIntConstrainedDefaultSetting { return NewTaskQueueTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -198,12 +209,13 @@ func GetIntPropertyFnFilteredByTaskQueue(value int) IntPropertyFnWithTaskQueueFi } type ShardIDIntSetting = ShardIDTypedSetting[int] +type ShardIDIntConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[int] func NewShardIDIntSetting(key Key, def int, description string) ShardIDIntSetting { return NewShardIDTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewShardIDIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) ShardIDIntSetting { +func NewShardIDIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) ShardIDIntConstrainedDefaultSetting { return NewShardIDTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -214,12 +226,13 @@ func GetIntPropertyFnFilteredByShardID(value int) IntPropertyFnWithShardIDFilter } type TaskTypeIntSetting = TaskTypeTypedSetting[int] +type TaskTypeIntConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[int] func NewTaskTypeIntSetting(key Key, def int, description string) TaskTypeIntSetting { return NewTaskTypeTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewTaskTypeIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) TaskTypeIntSetting { +func NewTaskTypeIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) TaskTypeIntConstrainedDefaultSetting { return NewTaskTypeTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -230,12 +243,13 @@ func GetIntPropertyFnFilteredByTaskType(value int) IntPropertyFnWithTaskTypeFilt } type DestinationIntSetting = DestinationTypedSetting[int] +type DestinationIntConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[int] func NewDestinationIntSetting(key Key, def int, description string) DestinationIntSetting { return NewDestinationTypedSettingWithConverter[int](key, convertInt, def, description) } -func NewDestinationIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) DestinationIntSetting { +func NewDestinationIntSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[int], description string) DestinationIntConstrainedDefaultSetting { return NewDestinationTypedSettingWithConstrainedDefault[int](key, convertInt, cdef, description) } @@ -246,12 +260,13 @@ func GetIntPropertyFnFilteredByDestination(value int) IntPropertyFnWithDestinati } type GlobalFloatSetting = GlobalTypedSetting[float64] +type GlobalFloatConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[float64] func NewGlobalFloatSetting(key Key, def float64, description string) GlobalFloatSetting { return NewGlobalTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewGlobalFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) GlobalFloatSetting { +func NewGlobalFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) GlobalFloatConstrainedDefaultSetting { return NewGlobalTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -262,12 +277,13 @@ func GetFloatPropertyFn(value float64) FloatPropertyFn { } type NamespaceFloatSetting = NamespaceTypedSetting[float64] +type NamespaceFloatConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[float64] func NewNamespaceFloatSetting(key Key, def float64, description string) NamespaceFloatSetting { return NewNamespaceTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewNamespaceFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) NamespaceFloatSetting { +func NewNamespaceFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) NamespaceFloatConstrainedDefaultSetting { return NewNamespaceTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -278,12 +294,13 @@ func GetFloatPropertyFnFilteredByNamespace(value float64) FloatPropertyFnWithNam } type NamespaceIDFloatSetting = NamespaceIDTypedSetting[float64] +type NamespaceIDFloatConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[float64] func NewNamespaceIDFloatSetting(key Key, def float64, description string) NamespaceIDFloatSetting { return NewNamespaceIDTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewNamespaceIDFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) NamespaceIDFloatSetting { +func NewNamespaceIDFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) NamespaceIDFloatConstrainedDefaultSetting { return NewNamespaceIDTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -294,12 +311,13 @@ func GetFloatPropertyFnFilteredByNamespaceID(value float64) FloatPropertyFnWithN } type TaskQueueFloatSetting = TaskQueueTypedSetting[float64] +type TaskQueueFloatConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[float64] func NewTaskQueueFloatSetting(key Key, def float64, description string) TaskQueueFloatSetting { return NewTaskQueueTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewTaskQueueFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) TaskQueueFloatSetting { +func NewTaskQueueFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) TaskQueueFloatConstrainedDefaultSetting { return NewTaskQueueTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -310,12 +328,13 @@ func GetFloatPropertyFnFilteredByTaskQueue(value float64) FloatPropertyFnWithTas } type ShardIDFloatSetting = ShardIDTypedSetting[float64] +type ShardIDFloatConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[float64] func NewShardIDFloatSetting(key Key, def float64, description string) ShardIDFloatSetting { return NewShardIDTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewShardIDFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) ShardIDFloatSetting { +func NewShardIDFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) ShardIDFloatConstrainedDefaultSetting { return NewShardIDTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -326,12 +345,13 @@ func GetFloatPropertyFnFilteredByShardID(value float64) FloatPropertyFnWithShard } type TaskTypeFloatSetting = TaskTypeTypedSetting[float64] +type TaskTypeFloatConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[float64] func NewTaskTypeFloatSetting(key Key, def float64, description string) TaskTypeFloatSetting { return NewTaskTypeTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewTaskTypeFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) TaskTypeFloatSetting { +func NewTaskTypeFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) TaskTypeFloatConstrainedDefaultSetting { return NewTaskTypeTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -342,12 +362,13 @@ func GetFloatPropertyFnFilteredByTaskType(value float64) FloatPropertyFnWithTask } type DestinationFloatSetting = DestinationTypedSetting[float64] +type DestinationFloatConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[float64] func NewDestinationFloatSetting(key Key, def float64, description string) DestinationFloatSetting { return NewDestinationTypedSettingWithConverter[float64](key, convertFloat, def, description) } -func NewDestinationFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) DestinationFloatSetting { +func NewDestinationFloatSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[float64], description string) DestinationFloatConstrainedDefaultSetting { return NewDestinationTypedSettingWithConstrainedDefault[float64](key, convertFloat, cdef, description) } @@ -358,12 +379,13 @@ func GetFloatPropertyFnFilteredByDestination(value float64) FloatPropertyFnWithD } type GlobalStringSetting = GlobalTypedSetting[string] +type GlobalStringConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[string] func NewGlobalStringSetting(key Key, def string, description string) GlobalStringSetting { return NewGlobalTypedSettingWithConverter[string](key, convertString, def, description) } -func NewGlobalStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) GlobalStringSetting { +func NewGlobalStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) GlobalStringConstrainedDefaultSetting { return NewGlobalTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -374,12 +396,13 @@ func GetStringPropertyFn(value string) StringPropertyFn { } type NamespaceStringSetting = NamespaceTypedSetting[string] +type NamespaceStringConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[string] func NewNamespaceStringSetting(key Key, def string, description string) NamespaceStringSetting { return NewNamespaceTypedSettingWithConverter[string](key, convertString, def, description) } -func NewNamespaceStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) NamespaceStringSetting { +func NewNamespaceStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) NamespaceStringConstrainedDefaultSetting { return NewNamespaceTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -390,12 +413,13 @@ func GetStringPropertyFnFilteredByNamespace(value string) StringPropertyFnWithNa } type NamespaceIDStringSetting = NamespaceIDTypedSetting[string] +type NamespaceIDStringConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[string] func NewNamespaceIDStringSetting(key Key, def string, description string) NamespaceIDStringSetting { return NewNamespaceIDTypedSettingWithConverter[string](key, convertString, def, description) } -func NewNamespaceIDStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) NamespaceIDStringSetting { +func NewNamespaceIDStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) NamespaceIDStringConstrainedDefaultSetting { return NewNamespaceIDTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -406,12 +430,13 @@ func GetStringPropertyFnFilteredByNamespaceID(value string) StringPropertyFnWith } type TaskQueueStringSetting = TaskQueueTypedSetting[string] +type TaskQueueStringConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[string] func NewTaskQueueStringSetting(key Key, def string, description string) TaskQueueStringSetting { return NewTaskQueueTypedSettingWithConverter[string](key, convertString, def, description) } -func NewTaskQueueStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) TaskQueueStringSetting { +func NewTaskQueueStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) TaskQueueStringConstrainedDefaultSetting { return NewTaskQueueTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -422,12 +447,13 @@ func GetStringPropertyFnFilteredByTaskQueue(value string) StringPropertyFnWithTa } type ShardIDStringSetting = ShardIDTypedSetting[string] +type ShardIDStringConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[string] func NewShardIDStringSetting(key Key, def string, description string) ShardIDStringSetting { return NewShardIDTypedSettingWithConverter[string](key, convertString, def, description) } -func NewShardIDStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) ShardIDStringSetting { +func NewShardIDStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) ShardIDStringConstrainedDefaultSetting { return NewShardIDTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -438,12 +464,13 @@ func GetStringPropertyFnFilteredByShardID(value string) StringPropertyFnWithShar } type TaskTypeStringSetting = TaskTypeTypedSetting[string] +type TaskTypeStringConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[string] func NewTaskTypeStringSetting(key Key, def string, description string) TaskTypeStringSetting { return NewTaskTypeTypedSettingWithConverter[string](key, convertString, def, description) } -func NewTaskTypeStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) TaskTypeStringSetting { +func NewTaskTypeStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) TaskTypeStringConstrainedDefaultSetting { return NewTaskTypeTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -454,12 +481,13 @@ func GetStringPropertyFnFilteredByTaskType(value string) StringPropertyFnWithTas } type DestinationStringSetting = DestinationTypedSetting[string] +type DestinationStringConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[string] func NewDestinationStringSetting(key Key, def string, description string) DestinationStringSetting { return NewDestinationTypedSettingWithConverter[string](key, convertString, def, description) } -func NewDestinationStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) DestinationStringSetting { +func NewDestinationStringSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[string], description string) DestinationStringConstrainedDefaultSetting { return NewDestinationTypedSettingWithConstrainedDefault[string](key, convertString, cdef, description) } @@ -470,12 +498,13 @@ func GetStringPropertyFnFilteredByDestination(value string) StringPropertyFnWith } type GlobalDurationSetting = GlobalTypedSetting[time.Duration] +type GlobalDurationConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[time.Duration] func NewGlobalDurationSetting(key Key, def time.Duration, description string) GlobalDurationSetting { return NewGlobalTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewGlobalDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) GlobalDurationSetting { +func NewGlobalDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) GlobalDurationConstrainedDefaultSetting { return NewGlobalTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -486,12 +515,13 @@ func GetDurationPropertyFn(value time.Duration) DurationPropertyFn { } type NamespaceDurationSetting = NamespaceTypedSetting[time.Duration] +type NamespaceDurationConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[time.Duration] func NewNamespaceDurationSetting(key Key, def time.Duration, description string) NamespaceDurationSetting { return NewNamespaceTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewNamespaceDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) NamespaceDurationSetting { +func NewNamespaceDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) NamespaceDurationConstrainedDefaultSetting { return NewNamespaceTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -502,12 +532,13 @@ func GetDurationPropertyFnFilteredByNamespace(value time.Duration) DurationPrope } type NamespaceIDDurationSetting = NamespaceIDTypedSetting[time.Duration] +type NamespaceIDDurationConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[time.Duration] func NewNamespaceIDDurationSetting(key Key, def time.Duration, description string) NamespaceIDDurationSetting { return NewNamespaceIDTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewNamespaceIDDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) NamespaceIDDurationSetting { +func NewNamespaceIDDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) NamespaceIDDurationConstrainedDefaultSetting { return NewNamespaceIDTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -518,12 +549,13 @@ func GetDurationPropertyFnFilteredByNamespaceID(value time.Duration) DurationPro } type TaskQueueDurationSetting = TaskQueueTypedSetting[time.Duration] +type TaskQueueDurationConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[time.Duration] func NewTaskQueueDurationSetting(key Key, def time.Duration, description string) TaskQueueDurationSetting { return NewTaskQueueTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewTaskQueueDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) TaskQueueDurationSetting { +func NewTaskQueueDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) TaskQueueDurationConstrainedDefaultSetting { return NewTaskQueueTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -534,12 +566,13 @@ func GetDurationPropertyFnFilteredByTaskQueue(value time.Duration) DurationPrope } type ShardIDDurationSetting = ShardIDTypedSetting[time.Duration] +type ShardIDDurationConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[time.Duration] func NewShardIDDurationSetting(key Key, def time.Duration, description string) ShardIDDurationSetting { return NewShardIDTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewShardIDDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) ShardIDDurationSetting { +func NewShardIDDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) ShardIDDurationConstrainedDefaultSetting { return NewShardIDTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -550,12 +583,13 @@ func GetDurationPropertyFnFilteredByShardID(value time.Duration) DurationPropert } type TaskTypeDurationSetting = TaskTypeTypedSetting[time.Duration] +type TaskTypeDurationConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[time.Duration] func NewTaskTypeDurationSetting(key Key, def time.Duration, description string) TaskTypeDurationSetting { return NewTaskTypeTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewTaskTypeDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) TaskTypeDurationSetting { +func NewTaskTypeDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) TaskTypeDurationConstrainedDefaultSetting { return NewTaskTypeTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -566,12 +600,13 @@ func GetDurationPropertyFnFilteredByTaskType(value time.Duration) DurationProper } type DestinationDurationSetting = DestinationTypedSetting[time.Duration] +type DestinationDurationConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[time.Duration] func NewDestinationDurationSetting(key Key, def time.Duration, description string) DestinationDurationSetting { return NewDestinationTypedSettingWithConverter[time.Duration](key, convertDuration, def, description) } -func NewDestinationDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) DestinationDurationSetting { +func NewDestinationDurationSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[time.Duration], description string) DestinationDurationConstrainedDefaultSetting { return NewDestinationTypedSettingWithConstrainedDefault[time.Duration](key, convertDuration, cdef, description) } @@ -582,12 +617,13 @@ func GetDurationPropertyFnFilteredByDestination(value time.Duration) DurationPro } type GlobalMapSetting = GlobalTypedSetting[map[string]any] +type GlobalMapConstrainedDefaultSetting = GlobalTypedConstrainedDefaultSetting[map[string]any] func NewGlobalMapSetting(key Key, def map[string]any, description string) GlobalMapSetting { return NewGlobalTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewGlobalMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) GlobalMapSetting { +func NewGlobalMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) GlobalMapConstrainedDefaultSetting { return NewGlobalTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -598,12 +634,13 @@ func GetMapPropertyFn(value map[string]any) MapPropertyFn { } type NamespaceMapSetting = NamespaceTypedSetting[map[string]any] +type NamespaceMapConstrainedDefaultSetting = NamespaceTypedConstrainedDefaultSetting[map[string]any] func NewNamespaceMapSetting(key Key, def map[string]any, description string) NamespaceMapSetting { return NewNamespaceTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewNamespaceMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) NamespaceMapSetting { +func NewNamespaceMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) NamespaceMapConstrainedDefaultSetting { return NewNamespaceTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -614,12 +651,13 @@ func GetMapPropertyFnFilteredByNamespace(value map[string]any) MapPropertyFnWith } type NamespaceIDMapSetting = NamespaceIDTypedSetting[map[string]any] +type NamespaceIDMapConstrainedDefaultSetting = NamespaceIDTypedConstrainedDefaultSetting[map[string]any] func NewNamespaceIDMapSetting(key Key, def map[string]any, description string) NamespaceIDMapSetting { return NewNamespaceIDTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewNamespaceIDMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) NamespaceIDMapSetting { +func NewNamespaceIDMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) NamespaceIDMapConstrainedDefaultSetting { return NewNamespaceIDTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -630,12 +668,13 @@ func GetMapPropertyFnFilteredByNamespaceID(value map[string]any) MapPropertyFnWi } type TaskQueueMapSetting = TaskQueueTypedSetting[map[string]any] +type TaskQueueMapConstrainedDefaultSetting = TaskQueueTypedConstrainedDefaultSetting[map[string]any] func NewTaskQueueMapSetting(key Key, def map[string]any, description string) TaskQueueMapSetting { return NewTaskQueueTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewTaskQueueMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) TaskQueueMapSetting { +func NewTaskQueueMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) TaskQueueMapConstrainedDefaultSetting { return NewTaskQueueTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -646,12 +685,13 @@ func GetMapPropertyFnFilteredByTaskQueue(value map[string]any) MapPropertyFnWith } type ShardIDMapSetting = ShardIDTypedSetting[map[string]any] +type ShardIDMapConstrainedDefaultSetting = ShardIDTypedConstrainedDefaultSetting[map[string]any] func NewShardIDMapSetting(key Key, def map[string]any, description string) ShardIDMapSetting { return NewShardIDTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewShardIDMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) ShardIDMapSetting { +func NewShardIDMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) ShardIDMapConstrainedDefaultSetting { return NewShardIDTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -662,12 +702,13 @@ func GetMapPropertyFnFilteredByShardID(value map[string]any) MapPropertyFnWithSh } type TaskTypeMapSetting = TaskTypeTypedSetting[map[string]any] +type TaskTypeMapConstrainedDefaultSetting = TaskTypeTypedConstrainedDefaultSetting[map[string]any] func NewTaskTypeMapSetting(key Key, def map[string]any, description string) TaskTypeMapSetting { return NewTaskTypeTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewTaskTypeMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) TaskTypeMapSetting { +func NewTaskTypeMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) TaskTypeMapConstrainedDefaultSetting { return NewTaskTypeTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -678,12 +719,13 @@ func GetMapPropertyFnFilteredByTaskType(value map[string]any) MapPropertyFnWithT } type DestinationMapSetting = DestinationTypedSetting[map[string]any] +type DestinationMapConstrainedDefaultSetting = DestinationTypedConstrainedDefaultSetting[map[string]any] func NewDestinationMapSetting(key Key, def map[string]any, description string) DestinationMapSetting { return NewDestinationTypedSettingWithConverter[map[string]any](key, convertMap, def, description) } -func NewDestinationMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) DestinationMapSetting { +func NewDestinationMapSettingWithConstrainedDefault(key Key, cdef []TypedConstrainedValue[map[string]any], description string) DestinationMapConstrainedDefaultSetting { return NewDestinationTypedSettingWithConstrainedDefault[map[string]any](key, convertMap, cdef, description) } @@ -694,10 +736,11 @@ func GetMapPropertyFnFilteredByDestination(value map[string]any) MapPropertyFnWi } type GlobalTypedSetting[T any] setting[T, func()] +type GlobalTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func()] // NewGlobalTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewGlobalTypedSetting[T any](key Key, def T, description string) GlobalTypedSetting[T] { s := GlobalTypedSetting[T]{ key: key, @@ -722,10 +765,10 @@ func NewGlobalTypedSettingWithConverter[T any](key Key, convert func(any) (T, er } // NewGlobalTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewGlobalTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) GlobalTypedSetting[T] { - s := GlobalTypedSetting[T]{ +func NewGlobalTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) GlobalTypedConstrainedDefaultSetting[T] { + s := GlobalTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -740,6 +783,13 @@ func (s GlobalTypedSetting[T]) Validate(v any) error { return err } +func (s GlobalTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s GlobalTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceGlobal } +func (s GlobalTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s GlobalTypedSetting[T]) WithDefault(v T) GlobalTypedSetting[T] { newS := s newS.def = v @@ -755,6 +805,18 @@ func (s GlobalTypedSetting[T]) Get(c *Collection) TypedPropertyFn[T] { c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s GlobalTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFn[T] { + return func() T { + prec := []Constraints{{}} + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -767,7 +829,7 @@ type TypedSubscribable[T any] func(callback func(T)) (v T, cancel func()) func (s GlobalTypedSetting[T]) Subscribe(c *Collection) TypedSubscribable[T] { return func(callback func(T)) (T, func()) { prec := []Constraints{{}} - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -781,6 +843,10 @@ func (s GlobalTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []Cons ) } +func (s GlobalTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFn[T any](value T) TypedPropertyFn[T] { return func() T { return value @@ -788,10 +854,11 @@ func GetTypedPropertyFn[T any](value T) TypedPropertyFn[T] { } type NamespaceTypedSetting[T any] setting[T, func(namespace string)] +type NamespaceTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func(namespace string)] // NewNamespaceTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewNamespaceTypedSetting[T any](key Key, def T, description string) NamespaceTypedSetting[T] { s := NamespaceTypedSetting[T]{ key: key, @@ -816,10 +883,10 @@ func NewNamespaceTypedSettingWithConverter[T any](key Key, convert func(any) (T, } // NewNamespaceTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewNamespaceTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) NamespaceTypedSetting[T] { - s := NamespaceTypedSetting[T]{ +func NewNamespaceTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) NamespaceTypedConstrainedDefaultSetting[T] { + s := NamespaceTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -834,6 +901,13 @@ func (s NamespaceTypedSetting[T]) Validate(v any) error { return err } +func (s NamespaceTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s NamespaceTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceNamespace } +func (s NamespaceTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s NamespaceTypedSetting[T]) WithDefault(v T) NamespaceTypedSetting[T] { newS := s newS.def = v @@ -849,6 +923,18 @@ func (s NamespaceTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithNamespac c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s NamespaceTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithNamespaceFilter[T] { + return func(namespace string) T { + prec := []Constraints{{Namespace: namespace}, {}} + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -861,7 +947,7 @@ type TypedSubscribableWithNamespaceFilter[T any] func(namespace string, callback func (s NamespaceTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithNamespaceFilter[T] { return func(namespace string, callback func(T)) (T, func()) { prec := []Constraints{{Namespace: namespace}, {}} - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -875,6 +961,10 @@ func (s NamespaceTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []C ) } +func (s NamespaceTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFnFilteredByNamespace[T any](value T) TypedPropertyFnWithNamespaceFilter[T] { return func(namespace string) T { return value @@ -882,10 +972,11 @@ func GetTypedPropertyFnFilteredByNamespace[T any](value T) TypedPropertyFnWithNa } type NamespaceIDTypedSetting[T any] setting[T, func(namespaceID namespace.ID)] +type NamespaceIDTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func(namespaceID namespace.ID)] // NewNamespaceIDTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewNamespaceIDTypedSetting[T any](key Key, def T, description string) NamespaceIDTypedSetting[T] { s := NamespaceIDTypedSetting[T]{ key: key, @@ -910,10 +1001,10 @@ func NewNamespaceIDTypedSettingWithConverter[T any](key Key, convert func(any) ( } // NewNamespaceIDTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewNamespaceIDTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) NamespaceIDTypedSetting[T] { - s := NamespaceIDTypedSetting[T]{ +func NewNamespaceIDTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) NamespaceIDTypedConstrainedDefaultSetting[T] { + s := NamespaceIDTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -928,6 +1019,13 @@ func (s NamespaceIDTypedSetting[T]) Validate(v any) error { return err } +func (s NamespaceIDTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s NamespaceIDTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceNamespaceID } +func (s NamespaceIDTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s NamespaceIDTypedSetting[T]) WithDefault(v T) NamespaceIDTypedSetting[T] { newS := s newS.def = v @@ -943,6 +1041,18 @@ func (s NamespaceIDTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithNamesp c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s NamespaceIDTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithNamespaceIDFilter[T] { + return func(namespaceID namespace.ID) T { + prec := []Constraints{{NamespaceID: namespaceID.String()}, {}} + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -955,7 +1065,7 @@ type TypedSubscribableWithNamespaceIDFilter[T any] func(namespaceID namespace.ID func (s NamespaceIDTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithNamespaceIDFilter[T] { return func(namespaceID namespace.ID, callback func(T)) (T, func()) { prec := []Constraints{{NamespaceID: namespaceID.String()}, {}} - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -969,6 +1079,10 @@ func (s NamespaceIDTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs [ ) } +func (s NamespaceIDTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFnFilteredByNamespaceID[T any](value T) TypedPropertyFnWithNamespaceIDFilter[T] { return func(namespaceID namespace.ID) T { return value @@ -976,10 +1090,11 @@ func GetTypedPropertyFnFilteredByNamespaceID[T any](value T) TypedPropertyFnWith } type TaskQueueTypedSetting[T any] setting[T, func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType)] +type TaskQueueTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType)] // NewTaskQueueTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewTaskQueueTypedSetting[T any](key Key, def T, description string) TaskQueueTypedSetting[T] { s := TaskQueueTypedSetting[T]{ key: key, @@ -1004,10 +1119,10 @@ func NewTaskQueueTypedSettingWithConverter[T any](key Key, convert func(any) (T, } // NewTaskQueueTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewTaskQueueTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) TaskQueueTypedSetting[T] { - s := TaskQueueTypedSetting[T]{ +func NewTaskQueueTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) TaskQueueTypedConstrainedDefaultSetting[T] { + s := TaskQueueTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -1022,6 +1137,13 @@ func (s TaskQueueTypedSetting[T]) Validate(v any) error { return err } +func (s TaskQueueTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s TaskQueueTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceTaskQueue } +func (s TaskQueueTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s TaskQueueTypedSetting[T]) WithDefault(v T) TaskQueueTypedSetting[T] { newS := s newS.def = v @@ -1043,6 +1165,24 @@ func (s TaskQueueTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskQueu c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s TaskQueueTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskQueueFilter[T] { + return func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType) T { + prec := []Constraints{ + {Namespace: namespace, TaskQueueName: taskQueue, TaskQueueType: taskQueueType}, + {Namespace: namespace, TaskQueueName: taskQueue}, + {TaskQueueName: taskQueue}, + {Namespace: namespace}, + {}, + } + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -1061,7 +1201,7 @@ func (s TaskQueueTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWith {Namespace: namespace}, {}, } - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -1075,6 +1215,10 @@ func (s TaskQueueTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []C ) } +func (s TaskQueueTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFnFilteredByTaskQueue[T any](value T) TypedPropertyFnWithTaskQueueFilter[T] { return func(namespace string, taskQueue string, taskQueueType enumspb.TaskQueueType) T { return value @@ -1082,10 +1226,11 @@ func GetTypedPropertyFnFilteredByTaskQueue[T any](value T) TypedPropertyFnWithTa } type ShardIDTypedSetting[T any] setting[T, func(shardID int32)] +type ShardIDTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func(shardID int32)] // NewShardIDTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewShardIDTypedSetting[T any](key Key, def T, description string) ShardIDTypedSetting[T] { s := ShardIDTypedSetting[T]{ key: key, @@ -1110,10 +1255,10 @@ func NewShardIDTypedSettingWithConverter[T any](key Key, convert func(any) (T, e } // NewShardIDTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewShardIDTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) ShardIDTypedSetting[T] { - s := ShardIDTypedSetting[T]{ +func NewShardIDTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) ShardIDTypedConstrainedDefaultSetting[T] { + s := ShardIDTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -1128,6 +1273,13 @@ func (s ShardIDTypedSetting[T]) Validate(v any) error { return err } +func (s ShardIDTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s ShardIDTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceShardID } +func (s ShardIDTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s ShardIDTypedSetting[T]) WithDefault(v T) ShardIDTypedSetting[T] { newS := s newS.def = v @@ -1143,6 +1295,18 @@ func (s ShardIDTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithShardIDFil c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s ShardIDTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithShardIDFilter[T] { + return func(shardID int32) T { + prec := []Constraints{{ShardID: shardID}, {}} + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -1155,7 +1319,7 @@ type TypedSubscribableWithShardIDFilter[T any] func(shardID int32, callback func func (s ShardIDTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithShardIDFilter[T] { return func(shardID int32, callback func(T)) (T, func()) { prec := []Constraints{{ShardID: shardID}, {}} - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -1169,6 +1333,10 @@ func (s ShardIDTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []Con ) } +func (s ShardIDTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFnFilteredByShardID[T any](value T) TypedPropertyFnWithShardIDFilter[T] { return func(shardID int32) T { return value @@ -1176,10 +1344,11 @@ func GetTypedPropertyFnFilteredByShardID[T any](value T) TypedPropertyFnWithShar } type TaskTypeTypedSetting[T any] setting[T, func(taskType enumsspb.TaskType)] +type TaskTypeTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func(taskType enumsspb.TaskType)] // NewTaskTypeTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewTaskTypeTypedSetting[T any](key Key, def T, description string) TaskTypeTypedSetting[T] { s := TaskTypeTypedSetting[T]{ key: key, @@ -1204,10 +1373,10 @@ func NewTaskTypeTypedSettingWithConverter[T any](key Key, convert func(any) (T, } // NewTaskTypeTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewTaskTypeTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) TaskTypeTypedSetting[T] { - s := TaskTypeTypedSetting[T]{ +func NewTaskTypeTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) TaskTypeTypedConstrainedDefaultSetting[T] { + s := TaskTypeTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -1222,6 +1391,13 @@ func (s TaskTypeTypedSetting[T]) Validate(v any) error { return err } +func (s TaskTypeTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s TaskTypeTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceTaskType } +func (s TaskTypeTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s TaskTypeTypedSetting[T]) WithDefault(v T) TaskTypeTypedSetting[T] { newS := s newS.def = v @@ -1237,6 +1413,18 @@ func (s TaskTypeTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskTypeF c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s TaskTypeTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithTaskTypeFilter[T] { + return func(taskType enumsspb.TaskType) T { + prec := []Constraints{{TaskType: taskType}, {}} + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -1249,7 +1437,7 @@ type TypedSubscribableWithTaskTypeFilter[T any] func(taskType enumsspb.TaskType, func (s TaskTypeTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWithTaskTypeFilter[T] { return func(taskType enumsspb.TaskType, callback func(T)) (T, func()) { prec := []Constraints{{TaskType: taskType}, {}} - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -1263,6 +1451,10 @@ func (s TaskTypeTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []Co ) } +func (s TaskTypeTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFnFilteredByTaskType[T any](value T) TypedPropertyFnWithTaskTypeFilter[T] { return func(taskType enumsspb.TaskType) T { return value @@ -1270,10 +1462,11 @@ func GetTypedPropertyFnFilteredByTaskType[T any](value T) TypedPropertyFnWithTas } type DestinationTypedSetting[T any] setting[T, func(namespace string, destination string)] +type DestinationTypedConstrainedDefaultSetting[T any] constrainedDefaultSetting[T, func(namespace string, destination string)] // NewDestinationTypedSetting creates a setting that uses mapstructure to handle complex structured -// values. The value from dynamic config will be copied over a shallow copy of 'def', which means -// 'def' must not contain any non-nil slices, maps, or pointers. +// values. The value from dynamic config will be _merged_ over a deep copy of 'def'. Be very careful +// when using non-empty maps or slices as defaults, the result may not be what you want. func NewDestinationTypedSetting[T any](key Key, def T, description string) DestinationTypedSetting[T] { s := DestinationTypedSetting[T]{ key: key, @@ -1298,10 +1491,10 @@ func NewDestinationTypedSettingWithConverter[T any](key Key, convert func(any) ( } // NewDestinationTypedSettingWithConstrainedDefault creates a setting with a compound default value. -func NewDestinationTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) DestinationTypedSetting[T] { - s := DestinationTypedSetting[T]{ +func NewDestinationTypedSettingWithConstrainedDefault[T any](key Key, convert func(any) (T, error), cdef []TypedConstrainedValue[T], description string) DestinationTypedConstrainedDefaultSetting[T] { + s := DestinationTypedConstrainedDefaultSetting[T]{ key: key, - cdef: &cdef, + cdef: cdef, convert: convert, description: description, } @@ -1316,6 +1509,13 @@ func (s DestinationTypedSetting[T]) Validate(v any) error { return err } +func (s DestinationTypedConstrainedDefaultSetting[T]) Key() Key { return s.key } +func (s DestinationTypedConstrainedDefaultSetting[T]) Precedence() Precedence { return PrecedenceDestination } +func (s DestinationTypedConstrainedDefaultSetting[T]) Validate(v any) error { + _, err := s.convert(v) + return err +} + func (s DestinationTypedSetting[T]) WithDefault(v T) DestinationTypedSetting[T] { newS := s newS.def = v @@ -1336,6 +1536,23 @@ func (s DestinationTypedSetting[T]) Get(c *Collection) TypedPropertyFnWithDestin c, s.key, s.def, + s.convert, + prec, + ) + } +} + +func (s DestinationTypedConstrainedDefaultSetting[T]) Get(c *Collection) TypedPropertyFnWithDestinationFilter[T] { + return func(namespace string, destination string) T { + prec := []Constraints{ + {Namespace: namespace, Destination: destination}, + {Destination: destination}, + {Namespace: namespace}, + {}, + } + return matchAndConvertWithConstrainedDefault( + c, + s.key, s.cdef, s.convert, prec, @@ -1353,7 +1570,7 @@ func (s DestinationTypedSetting[T]) Subscribe(c *Collection) TypedSubscribableWi {Namespace: namespace}, {}, } - return subscribe(c, s.key, s.def, s.cdef, s.convert, prec, callback) + return subscribe(c, s.key, s.def, s.convert, prec, callback) } } @@ -1367,8 +1584,13 @@ func (s DestinationTypedSetting[T]) dispatchUpdate(c *Collection, sub any, cvs [ ) } +func (s DestinationTypedConstrainedDefaultSetting[T]) dispatchUpdate(c *Collection, sub any, cvs []ConstrainedValue) { + // can't subscribe to constrained default settings +} + func GetTypedPropertyFnFilteredByDestination[T any](value T) TypedPropertyFnWithDestinationFilter[T] { return func(namespace string, destination string) T { return value } } + diff --git a/common/dynamicconfig/util.go b/common/dynamicconfig/util.go new file mode 100644 index 0000000000..e964a745fe --- /dev/null +++ b/common/dynamicconfig/util.go @@ -0,0 +1,23 @@ +package dynamicconfig + +import ( + "regexp" + + "github.com/mitchellh/mapstructure" + "go.temporal.io/server/common/util" +) + +var ( + MatchAnythingRE = regexp.MustCompile(".*") + MatchNothingRE = regexp.MustCompile(".^") +) + +func ConvertWildcardStringListToRegexp(in any) (*regexp.Regexp, error) { + // first convert raw value to list of strings + var patterns []string + if err := mapstructure.Decode(in, &patterns); err != nil { + return nil, err + } + // then turn strings into regexp + return util.WildCardStringsToRegexp(patterns) +} diff --git a/common/nexus/trace.go b/common/nexus/trace.go index e5b58f0a1d..40bbbe0084 100644 --- a/common/nexus/trace.go +++ b/common/nexus/trace.go @@ -22,12 +22,10 @@ type HTTPClientTraceProvider interface { } // HTTPTraceConfig is the dynamic config for controlling Nexus HTTP request tracing behavior. -// The default is nil and the conversion function does not do any actual conversion because this should be wrapped by -// a dynamicconfig.NewGlobalCachedTypedValue with the actual conversion function so that it is cached. var HTTPTraceConfig = dynamicconfig.NewGlobalTypedSettingWithConverter( "system.nexusHTTPTraceConfig", - func(a any) (any, error) { return a, nil }, - nil, + convertHTTPClientTraceConfig, + defaultHTTPClientTraceConfig, `Configuration options for controlling additional tracing for Nexus HTTP requests. Fields: Enabled, ForwardingEnabled, MinAttempt, MaxAttempt, Hooks. See HTTPClientTraceConfig comments for more detail.`, ) @@ -50,14 +48,16 @@ var defaultHTTPClientTraceConfig = HTTPClientTraceConfig{ ForwardingEnabled: false, MinAttempt: 2, MaxAttempt: 2, - // Set to nil here because of dynamic config conversion limitations. - Hooks: []string(nil), + // use separate default for Hooks so that users can override with a smaller set of hooks + Hooks: nil, } +var convertDefaultHTTPClientTraceConfig = dynamicconfig.ConvertStructure(defaultHTTPClientTraceConfig) + var defaultHTTPClientTraceHooks = []string{"GetConn", "GotConn", "ConnectStart", "ConnectDone", "DNSStart", "DNSDone", "TLSHandshakeStart", "TLSHandshakeDone", "WroteRequest", "GotFirstResponseByte"} func convertHTTPClientTraceConfig(in any) (HTTPClientTraceConfig, error) { - cfg, err := dynamicconfig.ConvertStructure(defaultHTTPClientTraceConfig)(in) + cfg, err := convertDefaultHTTPClientTraceConfig(in) if err != nil { cfg = defaultHTTPClientTraceConfig } @@ -68,17 +68,17 @@ func convertHTTPClientTraceConfig(in any) (HTTPClientTraceConfig, error) { } type LoggedHTTPClientTraceProvider struct { - Config *dynamicconfig.GlobalCachedTypedValue[HTTPClientTraceConfig] + Config dynamicconfig.TypedPropertyFn[HTTPClientTraceConfig] } func NewLoggedHTTPClientTraceProvider(dc *dynamicconfig.Collection) HTTPClientTraceProvider { return &LoggedHTTPClientTraceProvider{ - Config: dynamicconfig.NewGlobalCachedTypedValue(dc, HTTPTraceConfig, convertHTTPClientTraceConfig), + Config: HTTPTraceConfig.Get(dc), } } func (p *LoggedHTTPClientTraceProvider) NewTrace(attempt int32, logger log.Logger) *httptrace.ClientTrace { - config := p.Config.Get() + config := p.Config() if !config.Enabled { return nil } @@ -93,7 +93,7 @@ func (p *LoggedHTTPClientTraceProvider) NewTrace(attempt int32, logger log.Logge } func (p *LoggedHTTPClientTraceProvider) NewForwardingTrace(logger log.Logger) *httptrace.ClientTrace { - config := p.Config.Get() + config := p.Config() if !config.Enabled || !config.ForwardingEnabled { return nil } diff --git a/components/callbacks/config.go b/components/callbacks/config.go index 9bd89e404d..c4202a4696 100644 --- a/components/callbacks/config.go +++ b/components/callbacks/config.go @@ -68,7 +68,7 @@ func allowedAddressConverter(val any) ([]AddressMatchRule, error) { Pattern string AllowInsecure bool } - intermediate, err := dynamicconfig.ConvertStructure([]entry{})(val) + intermediate, err := dynamicconfig.ConvertStructure[[]entry](nil)(val) if err != nil { return nil, err } diff --git a/components/nexusoperations/config.go b/components/nexusoperations/config.go index d00d536ee7..c1d58c948c 100644 --- a/components/nexusoperations/config.go +++ b/components/nexusoperations/config.go @@ -1,7 +1,6 @@ package nexusoperations import ( - "slices" "strings" "time" @@ -74,20 +73,26 @@ ScheduleNexusOperation commands with a "nexus_header" field that exceeds this li Uses Go's len() function on header keys and values to determine the total size.`, ) -// defaultDisallowedOperationHeaders - set in the convert function below due to a limitation in the dynamic config framework. -// TODO: restore after an upgrade to Go 1.24 and merging #7052. -var defaultDisallowedOperationHeaders = []string{ - "request-timeout", - interceptor.DCRedirectionApiHeaderName, - interceptor.DCRedirectionContextHeaderName, - headers.CallerNameHeaderName, - headers.CallerTypeHeaderName, - headers.CallOriginHeaderName, -} - -var DisallowedOperationHeaders = dynamicconfig.NewGlobalTypedSetting( +var DisallowedOperationHeaders = dynamicconfig.NewGlobalTypedSettingWithConverter( "component.nexusoperations.disallowedHeaders", - []string(nil), + func(in any) ([]string, error) { + keys, err := dynamicconfig.ConvertStructure[[]string](nil)(in) + if err != nil { + return nil, err + } + for i, k := range keys { + keys[i] = strings.ToLower(k) + } + return keys, nil + }, + []string{ + "request-timeout", + interceptor.DCRedirectionApiHeaderName, + interceptor.DCRedirectionContextHeaderName, + headers.CallerNameHeaderName, + headers.CallerTypeHeaderName, + headers.CallOriginHeaderName, + }, `Case insensitive list of disallowed header keys for Nexus Operations. ScheduleNexusOperation commands with a "nexus_header" field that contains any of these disallowed keys will be rejected.`, @@ -161,26 +166,15 @@ type Config struct { func ConfigProvider(dc *dynamicconfig.Collection) *Config { return &Config{ - Enabled: dynamicconfig.EnableNexus.Get(dc), - RequestTimeout: RequestTimeout.Get(dc), - MinOperationTimeout: MinOperationTimeout.Get(dc), - MaxConcurrentOperations: MaxConcurrentOperations.Get(dc), - MaxServiceNameLength: MaxServiceNameLength.Get(dc), - MaxOperationNameLength: MaxOperationNameLength.Get(dc), - MaxOperationTokenLength: MaxOperationTokenLength.Get(dc), - MaxOperationHeaderSize: MaxOperationHeaderSize.Get(dc), - DisallowedOperationHeaders: dynamicconfig.NewGlobalCachedTypedValue(dc, DisallowedOperationHeaders, func(keys []string) ([]string, error) { - // Override with defaults unless explicitly set. - // Note that this prevents the ability to unset the config but that's an acceptable limitation. - if len(keys) == 0 { - keys = defaultDisallowedOperationHeaders - } - keys = slices.Clone(keys) - for i, k := range keys { - keys[i] = strings.ToLower(k) - } - return keys, nil - }).Get, + Enabled: dynamicconfig.EnableNexus.Get(dc), + RequestTimeout: RequestTimeout.Get(dc), + MinOperationTimeout: MinOperationTimeout.Get(dc), + MaxConcurrentOperations: MaxConcurrentOperations.Get(dc), + MaxServiceNameLength: MaxServiceNameLength.Get(dc), + MaxOperationNameLength: MaxOperationNameLength.Get(dc), + MaxOperationTokenLength: MaxOperationTokenLength.Get(dc), + MaxOperationHeaderSize: MaxOperationHeaderSize.Get(dc), + DisallowedOperationHeaders: DisallowedOperationHeaders.Get(dc), MaxOperationScheduleToCloseTimeout: MaxOperationScheduleToCloseTimeout.Get(dc), PayloadSizeLimit: dynamicconfig.BlobSizeLimitError.Get(dc), CallbackURLTemplate: CallbackURLTemplate.Get(dc), diff --git a/service/frontend/http_api_server.go b/service/frontend/http_api_server.go index 68e0bacc18..1ad2c8df28 100644 --- a/service/frontend/http_api_server.go +++ b/service/frontend/http_api_server.go @@ -44,7 +44,7 @@ type HTTPAPIServer struct { logger log.Logger serveMux *runtime.ServeMux stopped chan struct{} - allowedHosts *dynamicconfig.GlobalCachedTypedValue[*regexp.Regexp] + allowedHosts dynamicconfig.TypedPropertyFn[*regexp.Regexp] matchAdditionalHeaders map[string]bool matchAdditionalHeaderPrefixes []string } @@ -273,7 +273,7 @@ func (h *HTTPAPIServer) serveHTTP(w http.ResponseWriter, r *http.Request) { func (h *HTTPAPIServer) allowedHostsMiddleware(hf runtime.HandlerFunc) runtime.HandlerFunc { return func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) { - allowedHosts := h.allowedHosts.Get() + allowedHosts := h.allowedHosts() if allowedHosts.MatchString(r.Host) { hf(w, r, pathParams) return diff --git a/service/frontend/nexus_handler.go b/service/frontend/nexus_handler.go index 7f48a38311..c45ce81bdd 100644 --- a/service/frontend/nexus_handler.go +++ b/service/frontend/nexus_handler.go @@ -73,8 +73,8 @@ type operationContext struct { telemetryInterceptor *interceptor.TelemetryInterceptor redirectionInterceptor *interceptor.Redirection forwardingEnabledForNamespace dynamicconfig.BoolPropertyFnWithNamespaceFilter - headersBlacklist *dynamicconfig.GlobalCachedTypedValue[*regexp.Regexp] - metricTagConfig *dynamicconfig.GlobalCachedTypedValue[*nexusoperations.NexusMetricTagConfig] + headersBlacklist dynamicconfig.TypedPropertyFn[*regexp.Regexp] + metricTagConfig dynamicconfig.TypedPropertyFn[nexusoperations.NexusMetricTagConfig] cleanupFunctions []func(map[string]string, error) } @@ -226,7 +226,7 @@ func (c *operationContext) interceptRequest( if request.GetRequest().GetHeader() != nil { // Making a copy to ensure the original map is not modified as it might be used somewhere else. sanitizedHeaders := make(map[string]string, len(request.Request.Header)) - headersBlacklist := c.headersBlacklist.Get() + headersBlacklist := c.headersBlacklist() for name, value := range request.Request.Header { if !headersBlacklist.MatchString(name) { sanitizedHeaders[name] = value @@ -259,10 +259,7 @@ func (c *operationContext) shouldForwardRequest(ctx context.Context, header nexu // enrichNexusOperationMetrics enhances metrics with additional Nexus operation context based on configuration. func (c *operationContext) enrichNexusOperationMetrics(service, operation string, requestHeader nexus.Header) { - conf := c.metricTagConfig.Get() - if conf == nil { - return - } + conf := c.metricTagConfig() var tags []metrics.Tag @@ -301,8 +298,8 @@ type nexusHandler struct { forwardingEnabledForNamespace dynamicconfig.BoolPropertyFnWithNamespaceFilter forwardingClients *cluster.FrontendHTTPClientCache payloadSizeLimit dynamicconfig.IntPropertyFnWithNamespaceFilter - headersBlacklist *dynamicconfig.GlobalCachedTypedValue[*regexp.Regexp] - metricTagConfig *dynamicconfig.GlobalCachedTypedValue[*nexusoperations.NexusMetricTagConfig] + headersBlacklist dynamicconfig.TypedPropertyFn[*regexp.Regexp] + metricTagConfig dynamicconfig.TypedPropertyFn[nexusoperations.NexusMetricTagConfig] httpTraceProvider commonnexus.HTTPClientTraceProvider } diff --git a/service/frontend/nexus_handler_test.go b/service/frontend/nexus_handler_test.go index 53b53b8f2b..f80b78ae92 100644 --- a/service/frontend/nexus_handler_test.go +++ b/service/frontend/nexus_handler_test.go @@ -3,7 +3,6 @@ package frontend import ( "context" "errors" - "regexp" "testing" "time" @@ -146,21 +145,11 @@ func newOperationContext(options contextOptions) *operationContext { oc.forwardingEnabledForNamespace = dynamicconfig.GetBoolPropertyFnFilteredByNamespace( options.redirectAllow, ) - oc.headersBlacklist = dynamicconfig.NewGlobalCachedTypedValue( - dynamicconfig.NewCollection( - &dynamicconfig.StaticClient{ - dynamicconfig.FrontendNexusRequestHeadersBlacklist.Key(): options.headersBlacklist, - }, - nil, - ), - dynamicconfig.FrontendNexusRequestHeadersBlacklist, - func(patterns []string) (*regexp.Regexp, error) { - if len(patterns) == 0 { - return matchNothing, nil - } - return util.WildCardStringsToRegexp(patterns) - }, - ) + re, err := dynamicconfig.ConvertWildcardStringListToRegexp(options.headersBlacklist) + if err != nil { + panic(err) // nolint:forbidigo + } + oc.headersBlacklist = dynamicconfig.GetTypedPropertyFn(re) oc.redirectionInterceptor = interceptor.NewRedirection( nil, nil, diff --git a/service/frontend/service.go b/service/frontend/service.go index 7118a3b5cc..594aadfc5d 100644 --- a/service/frontend/service.go +++ b/service/frontend/service.go @@ -18,7 +18,6 @@ import ( "go.temporal.io/server/common/metrics" "go.temporal.io/server/common/persistence/visibility/manager" "go.temporal.io/server/common/retrypolicy" - "go.temporal.io/server/common/util" "go.temporal.io/server/components/callbacks" "go.temporal.io/server/components/nexusoperations" "google.golang.org/grpc" @@ -27,11 +26,6 @@ import ( "google.golang.org/grpc/reflection" ) -var ( - matchAny = regexp.MustCompile(".*") - matchNothing = regexp.MustCompile(".^") -) - // Config represents configuration for frontend service type Config struct { NumHistoryShards int32 @@ -190,8 +184,8 @@ type Config struct { CallbackEndpointConfigs dynamicconfig.TypedPropertyFnWithNamespaceFilter[[]callbacks.AddressMatchRule] MaxNexusOperationTokenLength dynamicconfig.IntPropertyFnWithNamespaceFilter - NexusRequestHeadersBlacklist *dynamicconfig.GlobalCachedTypedValue[*regexp.Regexp] - NexusOperationsMetricTagConfig *dynamicconfig.GlobalCachedTypedValue[*nexusoperations.NexusMetricTagConfig] + NexusRequestHeadersBlacklist dynamicconfig.TypedPropertyFn[*regexp.Regexp] + NexusOperationsMetricTagConfig dynamicconfig.TypedPropertyFn[nexusoperations.NexusMetricTagConfig] LinkMaxSize dynamicconfig.IntPropertyFnWithNamespaceFilter MaxLinksPerRequest dynamicconfig.IntPropertyFnWithNamespaceFilter @@ -215,7 +209,7 @@ type Config struct { ListWorkersEnabled dynamicconfig.BoolPropertyFnWithNamespaceFilter WorkerCommandsEnabled dynamicconfig.BoolPropertyFnWithNamespaceFilter - HTTPAllowedHosts *dynamicconfig.GlobalCachedTypedValue[*regexp.Regexp] + HTTPAllowedHosts dynamicconfig.TypedPropertyFn[*regexp.Regexp] } // NewConfig returns new service config with default values @@ -325,28 +319,13 @@ func NewConfig( EnableWorkerVersioningWorkflow: dynamicconfig.FrontendEnableWorkerVersioningWorkflowAPIs.Get(dc), EnableWorkerVersioningRules: dynamicconfig.FrontendEnableWorkerVersioningRuleAPIs.Get(dc), - EnableNexusAPIs: dynamicconfig.EnableNexus.Get(dc), - CallbackURLMaxLength: dynamicconfig.FrontendCallbackURLMaxLength.Get(dc), - CallbackHeaderMaxSize: dynamicconfig.FrontendCallbackHeaderMaxSize.Get(dc), - MaxCallbacksPerWorkflow: dynamicconfig.MaxCallbacksPerWorkflow.Get(dc), - MaxNexusOperationTokenLength: nexusoperations.MaxOperationTokenLength.Get(dc), - NexusRequestHeadersBlacklist: dynamicconfig.NewGlobalCachedTypedValue( - dc, - dynamicconfig.FrontendNexusRequestHeadersBlacklist, - func(patterns []string) (*regexp.Regexp, error) { - if len(patterns) == 0 { - return matchNothing, nil - } - return util.WildCardStringsToRegexp(patterns) - }, - ), - NexusOperationsMetricTagConfig: dynamicconfig.NewGlobalCachedTypedValue( - dc, - nexusoperations.MetricTagConfiguration, - func(config nexusoperations.NexusMetricTagConfig) (*nexusoperations.NexusMetricTagConfig, error) { - return &config, nil - }, - ), + EnableNexusAPIs: dynamicconfig.EnableNexus.Get(dc), + CallbackURLMaxLength: dynamicconfig.FrontendCallbackURLMaxLength.Get(dc), + CallbackHeaderMaxSize: dynamicconfig.FrontendCallbackHeaderMaxSize.Get(dc), + MaxCallbacksPerWorkflow: dynamicconfig.MaxCallbacksPerWorkflow.Get(dc), + MaxNexusOperationTokenLength: nexusoperations.MaxOperationTokenLength.Get(dc), + NexusRequestHeadersBlacklist: dynamicconfig.FrontendNexusRequestHeadersBlacklist.Get(dc), + NexusOperationsMetricTagConfig: nexusoperations.MetricTagConfiguration.Get(dc), LinkMaxSize: dynamicconfig.FrontendLinkMaxSize.Get(dc), MaxLinksPerRequest: dynamicconfig.FrontendMaxLinksPerRequest.Get(dc), @@ -366,12 +345,7 @@ func NewConfig( ListWorkersEnabled: dynamicconfig.ListWorkersEnabled.Get(dc), WorkerCommandsEnabled: dynamicconfig.WorkerCommandsEnabled.Get(dc), - HTTPAllowedHosts: dynamicconfig.NewGlobalCachedTypedValue(dc, dynamicconfig.FrontendHTTPAllowedHosts, func(patterns []string) (*regexp.Regexp, error) { - if len(patterns) == 0 { - return matchAny, nil - } - return util.WildCardStringsToRegexp(patterns) - }), + HTTPAllowedHosts: dynamicconfig.FrontendHTTPAllowedHosts.Get(dc), } }