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 }