2022-08-01 10:04:33 +08:00
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
2023-02-26 11:31:49 +08:00
// http://www.apache.org/licenses/LICENSE-2.0
2022-08-01 10:04:33 +08:00
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"fmt"
2022-08-03 01:12:33 +08:00
"strings"
2024-06-12 18:51:56 +08:00
"sync"
2022-08-01 10:04:33 +08:00
2023-02-26 11:31:49 +08:00
"github.com/cockroachdb/errors"
2022-08-01 10:04:33 +08:00
"go.uber.org/zap"
2023-04-06 19:14:32 +08:00
"github.com/milvus-io/milvus/pkg/log"
"github.com/milvus-io/milvus/pkg/util/typeutil"
2022-08-01 10:04:33 +08:00
)
const (
2022-11-17 18:59:09 +08:00
TombValue = "TOMB_VAULE"
2022-08-01 10:04:33 +08:00
)
2022-12-09 14:31:21 +08:00
type Filter func ( key string ) ( string , bool )
func WithSubstr ( substring string ) Filter {
substring = strings . ToLower ( substring )
return func ( key string ) ( string , bool ) {
return key , strings . Contains ( key , substring )
}
}
func WithPrefix ( prefix string ) Filter {
prefix = strings . ToLower ( prefix )
return func ( key string ) ( string , bool ) {
return key , strings . HasPrefix ( key , prefix )
}
}
func WithOneOfPrefixs ( prefixs ... string ) Filter {
for id , prefix := range prefixs {
prefixs [ id ] = strings . ToLower ( prefix )
}
return func ( key string ) ( string , bool ) {
for _ , prefix := range prefixs {
if strings . HasPrefix ( key , prefix ) {
return key , true
}
}
return key , false
}
}
func RemovePrefix ( prefix string ) Filter {
prefix = strings . ToLower ( prefix )
return func ( key string ) ( string , bool ) {
return strings . Replace ( key , prefix , "" , 1 ) , true
}
}
func filterate ( key string , filters ... Filter ) ( string , bool ) {
var ok bool
for _ , filter := range filters {
key , ok = filter ( key )
if ! ok {
return key , ok
}
}
return key , ok
}
2022-08-01 10:04:33 +08:00
type Manager struct {
2023-02-09 14:28:31 +08:00
Dispatcher * EventDispatcher
2024-01-11 13:48:49 +08:00
sources * typeutil . ConcurrentMap [ string , Source ]
keySourceMap * typeutil . ConcurrentMap [ string , string ] // store the key to config source, example: key is A.B.C and source is file which means the A.B.C's value is from file
overlays * typeutil . ConcurrentMap [ string , string ] // store the highest priority configs which modified at runtime
forbiddenKeys * typeutil . ConcurrentSet [ string ]
2024-06-12 18:51:56 +08:00
cacheMutex sync . RWMutex
configCache map [ string ] any
// configCache *typeutil.ConcurrentMap[string, interface{}]
2022-08-01 10:04:33 +08:00
}
func NewManager ( ) * Manager {
2024-03-12 11:09:04 +08:00
manager := & Manager {
2023-02-09 14:28:31 +08:00
Dispatcher : NewEventDispatcher ( ) ,
2024-01-11 13:48:49 +08:00
sources : typeutil . NewConcurrentMap [ string , Source ] ( ) ,
keySourceMap : typeutil . NewConcurrentMap [ string , string ] ( ) ,
overlays : typeutil . NewConcurrentMap [ string , string ] ( ) ,
forbiddenKeys : typeutil . NewConcurrentSet [ string ] ( ) ,
2024-06-12 18:51:56 +08:00
configCache : make ( map [ string ] any ) ,
2022-08-01 10:04:33 +08:00
}
2024-03-12 11:09:04 +08:00
resetConfigCacheFunc := NewHandler ( "reset.config.cache" , func ( event * Event ) {
keyToRemove := strings . NewReplacer ( "/" , "." ) . Replace ( event . Key )
2024-06-12 18:51:56 +08:00
manager . EvictCachedValue ( keyToRemove )
2024-03-12 11:09:04 +08:00
} )
manager . Dispatcher . RegisterForKeyPrefix ( "" , resetConfigCacheFunc )
return manager
}
func ( m * Manager ) GetCachedValue ( key string ) ( interface { } , bool ) {
2024-06-12 18:51:56 +08:00
m . cacheMutex . RLock ( )
defer m . cacheMutex . RUnlock ( )
value , ok := m . configCache [ key ]
return value , ok
2024-03-12 11:09:04 +08:00
}
2024-06-12 18:51:56 +08:00
func ( m * Manager ) CASCachedValue ( key string , origin string , value interface { } ) bool {
m . cacheMutex . Lock ( )
defer m . cacheMutex . Unlock ( )
current , err := m . GetConfig ( key )
2024-11-15 10:24:32 +08:00
if errors . Is ( err , ErrKeyNotFound ) {
m . configCache [ key ] = value
return true
}
if err != nil {
2024-06-12 18:51:56 +08:00
return false
}
if current != origin {
return false
}
m . configCache [ key ] = value
return true
2024-03-12 11:09:04 +08:00
}
func ( m * Manager ) EvictCachedValue ( key string ) {
2024-06-12 18:51:56 +08:00
m . cacheMutex . Lock ( )
defer m . cacheMutex . Unlock ( )
2024-08-21 11:02:56 +08:00
// cause param'value may rely on other params, so we need to evict all the cached value when config is changed
clear ( m . configCache )
2022-08-01 10:04:33 +08:00
}
2024-06-04 11:39:46 +08:00
func ( m * Manager ) EvictCacheValueByFormat ( keys ... string ) {
2024-06-12 18:51:56 +08:00
m . cacheMutex . Lock ( )
defer m . cacheMutex . Unlock ( )
2024-08-21 11:02:56 +08:00
// cause param'value may rely on other params, so we need to evict all the cached value when config is changed
clear ( m . configCache )
2024-06-04 11:39:46 +08:00
}
2022-08-01 10:04:33 +08:00
func ( m * Manager ) GetConfig ( key string ) ( string , error ) {
realKey := formatKey ( key )
2024-01-11 13:48:49 +08:00
v , ok := m . overlays . Get ( realKey )
2022-08-01 10:04:33 +08:00
if ok {
if v == TombValue {
2024-07-08 22:00:16 +08:00
return "" , errors . Wrap ( ErrKeyNotFound , key ) // fmt.Errorf("key not found %s", key)
2022-08-01 10:04:33 +08:00
}
return v , nil
}
2024-01-11 13:48:49 +08:00
sourceName , ok := m . keySourceMap . Get ( realKey )
2022-08-01 10:04:33 +08:00
if ! ok {
2024-07-08 22:00:16 +08:00
return "" , errors . Wrap ( ErrKeyNotFound , key ) // fmt.Errorf("key not found: %s", key)
2022-08-01 10:04:33 +08:00
}
return m . getConfigValueBySource ( realKey , sourceName )
}
2022-11-17 18:59:09 +08:00
// GetConfigs returns all the key values
func ( m * Manager ) GetConfigs ( ) map [ string ] string {
2022-08-01 10:04:33 +08:00
config := make ( map [ string ] string )
2024-01-11 13:48:49 +08:00
m . keySourceMap . Range ( func ( key , value string ) bool {
2023-01-06 09:55:36 +08:00
sValue , err := m . GetConfig ( key )
2022-08-01 10:04:33 +08:00
if err != nil {
2024-01-11 13:48:49 +08:00
return true
2022-08-01 10:04:33 +08:00
}
2024-01-11 13:48:49 +08:00
2022-08-01 10:04:33 +08:00
config [ key ] = sValue
2024-01-11 13:48:49 +08:00
return true
} )
m . overlays . Range ( func ( key , value string ) bool {
2023-07-25 17:23:01 +08:00
config [ key ] = value
2024-01-11 13:48:49 +08:00
return true
} )
2022-08-01 10:04:33 +08:00
return config
}
2023-01-13 15:31:41 +08:00
func ( m * Manager ) GetBy ( filters ... Filter ) map [ string ] string {
matchedConfig := make ( map [ string ] string )
2024-03-15 11:07:06 +08:00
m . keySourceMap . Range ( func ( key , value string ) bool {
2023-01-13 15:31:41 +08:00
newkey , ok := filterate ( key , filters ... )
2024-03-15 11:07:06 +08:00
if ! ok {
return true
2023-01-13 15:31:41 +08:00
}
2024-03-15 11:07:06 +08:00
sValue , err := m . GetConfig ( key )
if err != nil {
return true
}
matchedConfig [ newkey ] = sValue
return true
} )
m . overlays . Range ( func ( key , value string ) bool {
newkey , ok := filterate ( key , filters ... )
if ! ok {
return true
}
matchedConfig [ newkey ] = value
return true
} )
2023-01-13 15:31:41 +08:00
return matchedConfig
}
2023-02-23 15:33:45 +08:00
func ( m * Manager ) FileConfigs ( ) map [ string ] string {
config := make ( map [ string ] string )
2024-01-11 13:48:49 +08:00
m . sources . Range ( func ( key string , value Source ) bool {
if s , ok := value . ( * FileSource ) ; ok {
2023-02-23 15:33:45 +08:00
config , _ = s . GetConfigurations ( )
2024-01-11 13:48:49 +08:00
return false
2023-02-23 15:33:45 +08:00
}
2024-01-11 13:48:49 +08:00
return true
} )
2023-02-23 15:33:45 +08:00
return config
}
2022-08-01 10:04:33 +08:00
func ( m * Manager ) Close ( ) {
2024-01-11 13:48:49 +08:00
m . sources . Range ( func ( key string , value Source ) bool {
value . Close ( )
return true
} )
2022-08-01 10:04:33 +08:00
}
func ( m * Manager ) AddSource ( source Source ) error {
sourceName := source . GetSourceName ( )
2024-01-11 13:48:49 +08:00
_ , ok := m . sources . Get ( sourceName )
2022-08-01 10:04:33 +08:00
if ok {
err := errors . New ( "duplicate source supplied" )
return err
}
2024-06-04 11:39:46 +08:00
source . SetManager ( m )
2024-01-11 13:48:49 +08:00
m . sources . Insert ( sourceName , source )
2022-08-01 10:04:33 +08:00
err := m . pullSourceConfigs ( sourceName )
if err != nil {
err = fmt . Errorf ( "failed to load %s cause: %x" , sourceName , err )
return err
}
2022-12-26 19:11:30 +08:00
source . SetEventHandler ( m )
2022-08-01 10:04:33 +08:00
return nil
}
2023-02-09 14:28:31 +08:00
// Update config at runtime, which can be called by others
// The most used scenario is UT
2022-11-17 18:59:09 +08:00
func ( m * Manager ) SetConfig ( key , value string ) {
2024-01-11 13:48:49 +08:00
m . overlays . Insert ( formatKey ( key ) , value )
2022-11-17 18:59:09 +08:00
}
2023-11-28 15:32:31 +08:00
func ( m * Manager ) SetMapConfig ( key , value string ) {
2024-01-11 13:48:49 +08:00
m . overlays . Insert ( strings . ToLower ( key ) , value )
2023-11-28 15:32:31 +08:00
}
2023-02-09 14:28:31 +08:00
// Delete config at runtime, which has the highest priority to override all other sources
2022-11-17 18:59:09 +08:00
func ( m * Manager ) DeleteConfig ( key string ) {
2024-01-11 13:48:49 +08:00
m . overlays . Insert ( formatKey ( key ) , TombValue )
2022-11-17 18:59:09 +08:00
}
2023-02-09 14:28:31 +08:00
// Remove the config which set at runtime, use config from sources
2022-12-07 18:01:19 +08:00
func ( m * Manager ) ResetConfig ( key string ) {
2024-01-11 13:48:49 +08:00
m . overlays . Remove ( formatKey ( key ) )
2022-12-07 18:01:19 +08:00
}
2023-02-09 14:28:31 +08:00
// Ignore any of update events, which means the config cannot auto refresh anymore
func ( m * Manager ) ForbidUpdate ( key string ) {
m . forbiddenKeys . Insert ( formatKey ( key ) )
}
2023-05-06 17:34:39 +08:00
func ( m * Manager ) UpdateSourceOptions ( opts ... Option ) {
var options Options
for _ , opt := range opts {
opt ( & options )
}
2024-01-11 13:48:49 +08:00
m . sources . Range ( func ( key string , value Source ) bool {
value . UpdateOptions ( options )
return true
} )
2023-05-06 17:34:39 +08:00
}
2022-08-01 10:04:33 +08:00
// Do not use it directly, only used when add source and unittests.
func ( m * Manager ) pullSourceConfigs ( source string ) error {
2024-01-11 13:48:49 +08:00
configSource , ok := m . sources . Get ( source )
2022-08-01 10:04:33 +08:00
if ! ok {
return errors . New ( "invalid source or source not added" )
}
configs , err := configSource . GetConfigurations ( )
if err != nil {
2023-11-08 23:34:22 +08:00
log . Info ( "Get configuration by items failed" , zap . Error ( err ) )
2022-08-01 10:04:33 +08:00
return err
}
sourcePriority := configSource . GetPriority ( )
for key := range configs {
2024-01-11 13:48:49 +08:00
sourceName , ok := m . keySourceMap . Get ( key )
2022-08-01 10:04:33 +08:00
if ! ok { // if key do not exist then add source
2024-01-11 13:48:49 +08:00
m . keySourceMap . Insert ( key , source )
2022-08-01 10:04:33 +08:00
continue
}
2024-01-11 13:48:49 +08:00
currentSource , ok := m . sources . Get ( sourceName )
2022-08-01 10:04:33 +08:00
if ! ok {
2024-01-11 13:48:49 +08:00
m . keySourceMap . Insert ( key , source )
2022-08-01 10:04:33 +08:00
continue
}
currentSrcPriority := currentSource . GetPriority ( )
if currentSrcPriority > sourcePriority { // lesser value has high priority
2024-01-11 13:48:49 +08:00
m . keySourceMap . Insert ( key , source )
2022-08-01 10:04:33 +08:00
}
}
return nil
}
func ( m * Manager ) getConfigValueBySource ( configKey , sourceName string ) ( string , error ) {
2024-01-11 13:48:49 +08:00
source , ok := m . sources . Get ( sourceName )
2022-08-01 10:04:33 +08:00
if ! ok {
return "" , ErrKeyNotFound
}
return source . GetConfigurationByKey ( configKey )
}
func ( m * Manager ) updateEvent ( e * Event ) error {
// refresh all configuration one by one
if e . HasUpdated {
return nil
}
switch e . EventType {
case CreateType , UpdateType :
2024-01-11 13:48:49 +08:00
sourceName , ok := m . keySourceMap . Get ( e . Key )
2022-08-01 10:04:33 +08:00
if ! ok {
2024-01-11 13:48:49 +08:00
m . keySourceMap . Insert ( e . Key , e . EventSource )
2022-08-01 10:04:33 +08:00
e . EventType = CreateType
} else if sourceName == e . EventSource {
e . EventType = UpdateType
} else if sourceName != e . EventSource {
prioritySrc := m . getHighPrioritySource ( sourceName , e . EventSource )
if prioritySrc != nil && prioritySrc . GetSourceName ( ) == sourceName {
// if event generated from less priority source then ignore
log . Info ( fmt . Sprintf ( "the event source %s's priority is less then %s's, ignore" ,
e . EventSource , sourceName ) )
return ErrIgnoreChange
}
2024-01-11 13:48:49 +08:00
m . keySourceMap . Insert ( e . Key , e . EventSource )
2022-08-01 10:04:33 +08:00
e . EventType = UpdateType
}
case DeleteType :
2024-01-11 13:48:49 +08:00
sourceName , ok := m . keySourceMap . Get ( e . Key )
2022-08-01 10:04:33 +08:00
if ! ok || sourceName != e . EventSource {
// if delete event generated from source not maintained ignore it
log . Info ( fmt . Sprintf ( "the event source %s (expect %s) is not maintained, ignore" ,
e . EventSource , sourceName ) )
return ErrIgnoreChange
} else if sourceName == e . EventSource {
// find less priority source or delete key
source := m . findNextBestSource ( e . Key , sourceName )
if source == nil {
2024-01-11 13:48:49 +08:00
m . keySourceMap . Remove ( e . Key )
2022-08-01 10:04:33 +08:00
} else {
2024-01-11 13:48:49 +08:00
m . keySourceMap . Insert ( e . Key , source . GetSourceName ( ) )
2022-08-01 10:04:33 +08:00
}
}
}
2023-09-05 10:31:48 +08:00
log . Info ( "receive update event" , zap . Any ( "event" , e ) )
2022-08-01 10:04:33 +08:00
e . HasUpdated = true
return nil
}
// OnEvent Triggers actions when an event is generated
func ( m * Manager ) OnEvent ( event * Event ) {
2023-02-09 14:28:31 +08:00
if m . forbiddenKeys . Contain ( formatKey ( event . Key ) ) {
log . Info ( "ignore event for forbidden key" , zap . String ( "key" , event . Key ) )
return
}
2022-08-01 10:04:33 +08:00
err := m . updateEvent ( event )
if err != nil {
log . Warn ( "failed in updating event with error" , zap . Error ( err ) , zap . Any ( "event" , event ) )
return
}
2022-11-17 18:59:09 +08:00
m . Dispatcher . Dispatch ( event )
}
func ( m * Manager ) GetIdentifier ( ) string {
return "Manager"
2022-08-01 10:04:33 +08:00
}
2024-01-11 13:48:49 +08:00
func ( m * Manager ) findNextBestSource ( configKey string , sourceName string ) Source {
2022-08-01 10:04:33 +08:00
var rSource Source
2024-01-11 13:48:49 +08:00
m . sources . Range ( func ( key string , value Source ) bool {
if value . GetSourceName ( ) == sourceName {
return true
2022-08-01 10:04:33 +08:00
}
2024-01-11 13:48:49 +08:00
_ , err := value . GetConfigurationByKey ( configKey )
2022-08-01 10:04:33 +08:00
if err != nil {
2024-01-11 13:48:49 +08:00
return true
2022-08-01 10:04:33 +08:00
}
if rSource == nil {
2024-01-11 13:48:49 +08:00
rSource = value
return true
2022-08-01 10:04:33 +08:00
}
2024-01-11 13:48:49 +08:00
if value . GetPriority ( ) < rSource . GetPriority ( ) { // less value has high priority
rSource = value
2022-08-01 10:04:33 +08:00
}
2024-01-11 13:48:49 +08:00
return true
} )
2022-08-01 10:04:33 +08:00
return rSource
}
func ( m * Manager ) getHighPrioritySource ( srcNameA , srcNameB string ) Source {
2024-01-11 13:48:49 +08:00
sourceA , okA := m . sources . Get ( srcNameA )
sourceB , okB := m . sources . Get ( srcNameB )
2022-08-01 10:04:33 +08:00
if ! okA && ! okB {
return nil
} else if ! okA {
return sourceB
} else if ! okB {
return sourceA
}
2023-09-21 09:45:27 +08:00
if sourceA . GetPriority ( ) < sourceB . GetPriority ( ) { // less value has high priority
2022-08-01 10:04:33 +08:00
return sourceA
}
return sourceB
}