github.com/polarismesh/polaris@v1.17.8/cache/config/config_group.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package config
    19  
    20  import (
    21  	"sort"
    22  	"strings"
    23  	"time"
    24  
    25  	"go.uber.org/zap"
    26  	"golang.org/x/sync/singleflight"
    27  
    28  	types "github.com/polarismesh/polaris/cache/api"
    29  	"github.com/polarismesh/polaris/common/model"
    30  	"github.com/polarismesh/polaris/common/utils"
    31  	"github.com/polarismesh/polaris/store"
    32  )
    33  
    34  type configGroupCache struct {
    35  	*types.BaseCache
    36  	storage store.Store
    37  	// files config_file_group.id -> model.ConfigFileGroup
    38  	groups *utils.SyncMap[uint64, *model.ConfigFileGroup]
    39  	// name2files config_file.<namespace, group> -> model.ConfigFileGroup
    40  	name2groups *utils.SyncMap[string, *utils.SyncMap[string, *model.ConfigFileGroup]]
    41  	// singleGroup
    42  	singleGroup *singleflight.Group
    43  }
    44  
    45  // NewConfigGroupCache 创建文件缓存
    46  func NewConfigGroupCache(storage store.Store, cacheMgr types.CacheManager) types.ConfigGroupCache {
    47  	cache := &configGroupCache{
    48  		BaseCache: types.NewBaseCache(storage, cacheMgr),
    49  		storage:   storage,
    50  	}
    51  	return cache
    52  }
    53  
    54  // Initialize
    55  func (fc *configGroupCache) Initialize(opt map[string]interface{}) error {
    56  	fc.groups = utils.NewSyncMap[uint64, *model.ConfigFileGroup]()
    57  	fc.name2groups = utils.NewSyncMap[string, *utils.SyncMap[string, *model.ConfigFileGroup]]()
    58  	fc.singleGroup = &singleflight.Group{}
    59  	return nil
    60  }
    61  
    62  // Update 更新缓存函数
    63  func (fc *configGroupCache) Update() error {
    64  	err, _ := fc.singleUpdate()
    65  	return err
    66  }
    67  
    68  func (fc *configGroupCache) singleUpdate() (error, bool) {
    69  	// 多个线程竞争,只有一个线程进行更新
    70  	_, err, shared := fc.singleGroup.Do(fc.Name(), func() (interface{}, error) {
    71  		return nil, fc.DoCacheUpdate(fc.Name(), fc.realUpdate)
    72  	})
    73  	return err, shared
    74  }
    75  
    76  func (fc *configGroupCache) realUpdate() (map[string]time.Time, int64, error) {
    77  	start := time.Now()
    78  	groups, err := fc.storage.GetMoreConfigGroup(fc.IsFirstUpdate(), fc.LastFetchTime())
    79  	if err != nil {
    80  		return nil, 0, err
    81  	}
    82  	if len(groups) == 0 {
    83  		return nil, 0, nil
    84  	}
    85  	lastMimes, update, del := fc.setConfigGroups(groups)
    86  	log.Info("[Cache][ConfigGroup] get more config_groups",
    87  		zap.Int("update", update), zap.Int("delete", del),
    88  		zap.Time("last", fc.LastMtime()), zap.Duration("used", time.Since(start)))
    89  	return lastMimes, int64(len(groups)), err
    90  }
    91  
    92  func (fc *configGroupCache) LastMtime() time.Time {
    93  	return fc.BaseCache.LastMtime(fc.Name())
    94  }
    95  
    96  func (fc *configGroupCache) setConfigGroups(groups []*model.ConfigFileGroup) (map[string]time.Time, int, int) {
    97  	lastMtime := fc.LastMtime().Unix()
    98  	update := 0
    99  	del := 0
   100  
   101  	affect := map[string]struct{}{}
   102  
   103  	for i := range groups {
   104  		item := groups[i]
   105  		affect[item.Namespace] = struct{}{}
   106  
   107  		if !item.Valid {
   108  			del++
   109  			fc.groups.Delete(item.Id)
   110  			nsBucket, ok := fc.name2groups.Load(item.Namespace)
   111  			if ok {
   112  				nsBucket.Delete(item.Name)
   113  			}
   114  		} else {
   115  			update++
   116  			fc.groups.Store(item.Id, item)
   117  			if _, ok := fc.name2groups.Load(item.Namespace); !ok {
   118  				fc.name2groups.Store(item.Namespace, utils.NewSyncMap[string, *model.ConfigFileGroup]())
   119  			}
   120  			nsBucket, _ := fc.name2groups.Load(item.Namespace)
   121  			nsBucket.Store(item.Name, item)
   122  		}
   123  
   124  		modifyUnix := item.ModifyTime.Unix()
   125  		if modifyUnix > lastMtime {
   126  			lastMtime = modifyUnix
   127  		}
   128  	}
   129  
   130  	fc.postProcessUpdatedGroups(affect)
   131  
   132  	return map[string]time.Time{
   133  		fc.Name(): time.Unix(lastMtime, 0),
   134  	}, update, del
   135  }
   136  
   137  // postProcessUpdatedGroups
   138  func (fc *configGroupCache) postProcessUpdatedGroups(affect map[string]struct{}) {
   139  	for ns := range affect {
   140  		nsBucket, ok := fc.name2groups.Load(ns)
   141  		if ok {
   142  			count := nsBucket.Len()
   143  			fc.reportMetricsInfo(ns, count)
   144  		}
   145  	}
   146  }
   147  
   148  // Clear
   149  func (fc *configGroupCache) Clear() error {
   150  	fc.groups = utils.NewSyncMap[uint64, *model.ConfigFileGroup]()
   151  	fc.name2groups = utils.NewSyncMap[string, *utils.SyncMap[string, *model.ConfigFileGroup]]()
   152  	fc.singleGroup = &singleflight.Group{}
   153  	return nil
   154  }
   155  
   156  // Name
   157  func (fc *configGroupCache) Name() string {
   158  	return types.ConfigGroupCacheName
   159  }
   160  
   161  // GetGroupByName
   162  func (fc *configGroupCache) GetGroupByName(namespace, name string) *model.ConfigFileGroup {
   163  	nsBucket, ok := fc.name2groups.Load(namespace)
   164  	if !ok {
   165  		return nil
   166  	}
   167  
   168  	val, _ := nsBucket.Load(name)
   169  	return val
   170  }
   171  
   172  // GetGroupByID
   173  func (fc *configGroupCache) GetGroupByID(id uint64) *model.ConfigFileGroup {
   174  	val, _ := fc.groups.Load(id)
   175  	return val
   176  }
   177  
   178  // forceQueryUpdate 为了确保读取的数据是最新的,这里需要做一个强制 update 的动作进行数据读取处理
   179  func (fc *configGroupCache) forceQueryUpdate() error {
   180  	err, shared := fc.singleUpdate()
   181  	// shared == true,表示当前已经有正在 update 执行的任务,这个任务不一定能够读取到最新的数据
   182  	// 为了避免读取到脏数据,在发起一次 singleUpdate
   183  	if shared {
   184  		configLog.Debug("[Config][Group][Query] force query update second")
   185  		err, _ = fc.singleUpdate()
   186  	}
   187  	return err
   188  }
   189  
   190  // Query
   191  func (fc *configGroupCache) Query(args *types.ConfigGroupArgs) (uint32, []*model.ConfigFileGroup, error) {
   192  	if err := fc.forceQueryUpdate(); err != nil {
   193  		return 0, nil, err
   194  	}
   195  
   196  	values := make([]*model.ConfigFileGroup, 0, 8)
   197  	fc.name2groups.Range(func(namespce string, groups *utils.SyncMap[string, *model.ConfigFileGroup]) bool {
   198  		if args.Namespace != "" && utils.IsWildNotMatch(namespce, args.Namespace) {
   199  			return true
   200  		}
   201  		groups.Range(func(name string, group *model.ConfigFileGroup) bool {
   202  			if args.Name != "" && utils.IsWildNotMatch(name, args.Name) {
   203  				return true
   204  			}
   205  			if args.Business != "" && utils.IsWildNotMatch(group.Business, args.Business) {
   206  				return true
   207  			}
   208  			if args.Department != "" && utils.IsWildNotMatch(group.Department, args.Department) {
   209  				return true
   210  			}
   211  			if len(args.Metadata) > 0 {
   212  				for k, v := range args.Metadata {
   213  					sv, ok := group.Metadata[k]
   214  					if !ok || sv != v {
   215  						return true
   216  					}
   217  				}
   218  			}
   219  			values = append(values, group)
   220  			return true
   221  		})
   222  		return true
   223  	})
   224  
   225  	sort.Slice(values, func(i, j int) bool {
   226  		asc := strings.ToLower(args.OrderType) == "asc" || args.OrderType == ""
   227  		if strings.ToLower(args.OrderField) == "name" {
   228  			return orderByConfigGroupName(values[i], values[j], asc)
   229  		}
   230  		return orderByConfigGroupMtime(values[i], values[j], asc)
   231  	})
   232  
   233  	return uint32(len(values)), doPageConfigGroups(values, args.Offset, args.Limit), nil
   234  }
   235  
   236  func orderByConfigGroupName(a, b *model.ConfigFileGroup, asc bool) bool {
   237  	if a.Name < b.Name {
   238  		return asc
   239  	}
   240  	if a.Name > b.Name {
   241  		// false && asc always false
   242  		return false
   243  	}
   244  	return a.Id < b.Id && asc
   245  }
   246  
   247  func orderByConfigGroupMtime(a, b *model.ConfigFileGroup, asc bool) bool {
   248  	if a.ModifyTime.After(b.ModifyTime) {
   249  		return asc
   250  	}
   251  	if a.ModifyTime.Before(b.ModifyTime) {
   252  		// false && asc always false
   253  		return false
   254  	}
   255  	return a.Id < b.Id && asc
   256  }
   257  
   258  func doPageConfigGroups(ret []*model.ConfigFileGroup, offset, limit uint32) []*model.ConfigFileGroup {
   259  	amount := uint32(len(ret))
   260  	if offset >= amount || limit == 0 {
   261  		return nil
   262  	}
   263  	endIdx := offset + limit
   264  	if endIdx > amount {
   265  		endIdx = amount
   266  	}
   267  	return ret[offset:endIdx]
   268  }