github.com/polarismesh/polaris@v1.17.8/cache/config/config_file.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 "errors" 22 "os" 23 "path/filepath" 24 "sort" 25 "strings" 26 "time" 27 28 "go.etcd.io/bbolt" 29 "go.uber.org/zap" 30 "golang.org/x/sync/singleflight" 31 32 types "github.com/polarismesh/polaris/cache/api" 33 "github.com/polarismesh/polaris/common/eventhub" 34 "github.com/polarismesh/polaris/common/model" 35 "github.com/polarismesh/polaris/common/utils" 36 "github.com/polarismesh/polaris/store" 37 ) 38 39 // FileCache 文件缓存,使用 loading cache 懒加载策略。同时写入时设置过期时间,定时清理过期的缓存。 40 type fileCache struct { 41 *types.BaseCache 42 storage store.Store 43 // releases config_release.id -> model.SimpleConfigFileRelease 44 releases *utils.SegmentMap[uint64, *model.SimpleConfigFileRelease] 45 // name2release namespace -> group -> file_name -> []model.ConfigFileRelease 46 name2release *utils.SyncMap[string, *utils.SyncMap[string, *utils.SyncMap[string, 47 *utils.SyncMap[string, *model.SimpleConfigFileRelease]]]] 48 // activeReleases namespace -> group -> []model.ConfigFileRelease 49 activeReleases *utils.SyncMap[string, *utils.SyncMap[string, *utils.SyncMap[string, *model.SimpleConfigFileRelease]]] 50 // groupedActiveReleaseRevisions namespace -> group -> revision 51 activeReleaseRevisions *utils.SyncMap[string, *utils.SyncMap[string, string]] 52 // singleGroup 53 singleGroup *singleflight.Group 54 // valueCache save ConfigFileRelease.Content into local file to reduce memory use 55 valueCache *bbolt.DB 56 // metricsReleaseCount 57 metricsReleaseCount *utils.SyncMap[string, *utils.SyncMap[string, uint64]] 58 // preMetricsFiles 59 preMetricsFiles *utils.AtomicValue[map[string]map[string]struct{}] 60 // lastReportTime 61 lastReportTime *utils.AtomicValue[time.Time] 62 } 63 64 // NewConfigFileCache 创建文件缓存 65 func NewConfigFileCache(storage store.Store, cacheMgr types.CacheManager) types.ConfigFileCache { 66 cache := &fileCache{ 67 BaseCache: types.NewBaseCache(storage, cacheMgr), 68 storage: storage, 69 } 70 return cache 71 } 72 73 // Initialize 74 func (fc *fileCache) Initialize(opt map[string]interface{}) error { 75 fc.releases = utils.NewSegmentMap[uint64, *model.SimpleConfigFileRelease](1, func(k uint64) int { 76 return int(k) 77 }) 78 fc.name2release = utils.NewSyncMap[string, *utils.SyncMap[string, *utils.SyncMap[string, 79 *utils.SyncMap[string, *model.SimpleConfigFileRelease]]]]() 80 fc.activeReleases = utils.NewSyncMap[string, *utils.SyncMap[string, 81 *utils.SyncMap[string, *model.SimpleConfigFileRelease]]]() 82 fc.activeReleaseRevisions = utils.NewSyncMap[string, *utils.SyncMap[string, string]]() 83 fc.singleGroup = &singleflight.Group{} 84 fc.metricsReleaseCount = utils.NewSyncMap[string, *utils.SyncMap[string, uint64]]() 85 fc.preMetricsFiles = utils.NewAtomicValue[map[string]map[string]struct{}](map[string]map[string]struct{}{}) 86 fc.lastReportTime = utils.NewAtomicValue[time.Time](time.Time{}) 87 valueCache, err := openBoltCache(opt) 88 if err != nil { 89 return err 90 } 91 fc.valueCache = valueCache 92 return nil 93 } 94 95 func openBoltCache(opt map[string]interface{}) (*bbolt.DB, error) { 96 path, _ := opt["cachePath"].(string) 97 if path == "" { 98 path = "./data/cache/config" 99 } 100 if err := os.MkdirAll(path, os.ModePerm); err != nil { 101 return nil, err 102 } 103 dbFile := filepath.Join(path, "config_file.bolt") 104 _ = os.Remove(dbFile) 105 valueCache, err := bbolt.Open(dbFile, os.ModePerm, &bbolt.Options{}) 106 if err != nil { 107 return nil, err 108 } 109 return valueCache, nil 110 } 111 112 // Update 更新缓存函数 113 func (fc *fileCache) Update() error { 114 err, _ := fc.singleUpdate() 115 return err 116 } 117 118 func (fc *fileCache) singleUpdate() (error, bool) { 119 // 多个线程竞争,只有一个线程进行更新 120 _, err, shared := fc.singleGroup.Do(fc.Name(), func() (interface{}, error) { 121 defer func() { 122 fc.reportMetricsInfo() 123 }() 124 return nil, fc.DoCacheUpdate(fc.Name(), fc.realUpdate) 125 }) 126 return err, shared 127 } 128 129 func (fc *fileCache) realUpdate() (map[string]time.Time, int64, error) { 130 // 拉取diff前的所有数据 131 start := time.Now() 132 releases, err := fc.storage.GetMoreReleaseFile(fc.IsFirstUpdate(), fc.LastFetchTime()) 133 if err != nil { 134 return nil, 0, err 135 } 136 if len(releases) == 0 { 137 return nil, 0, nil 138 } 139 140 lastMimes, update, del, err := fc.setReleases(releases) 141 if err != nil { 142 return nil, 0, err 143 } 144 log.Info("[Cache][ConfigReleases] get more releases", 145 zap.Int("update", update), zap.Int("delete", del), 146 zap.Time("last", fc.LastMtime()), zap.Duration("used", time.Since(start))) 147 return lastMimes, int64(len(releases)), err 148 } 149 150 func (fc *fileCache) setReleases(releases []*model.ConfigFileRelease) (map[string]time.Time, int, int, error) { 151 lastMtime := fc.LastMtime().Unix() 152 update := 0 153 del := 0 154 155 affect := map[string]map[string]struct{}{} 156 157 for i := range releases { 158 item := releases[i] 159 if _, ok := affect[item.Namespace]; !ok { 160 affect[item.Namespace] = map[string]struct{}{} 161 } 162 affect[item.Namespace][item.Group] = struct{}{} 163 164 modifyUnix := item.ModifyTime.Unix() 165 if modifyUnix > lastMtime { 166 lastMtime = modifyUnix 167 } 168 oldVal, _ := fc.releases.Get(item.Id) 169 if !item.Valid { 170 del++ 171 if err := fc.handleDeleteRelease(oldVal, item); err != nil { 172 return nil, update, del, err 173 } 174 } else { 175 update++ 176 if err := fc.handleUpdateRelease(oldVal, item); err != nil { 177 return nil, update, del, err 178 } 179 } 180 181 if item.Active { 182 configLog.Info("[Config][Release][Cache] notify config release change", 183 zap.Any("info", item.SimpleConfigFileRelease)) 184 fc.sendEvent(item) 185 } 186 } 187 fc.postProcessUpdatedRelease(affect) 188 return map[string]time.Time{fc.Name(): time.Unix(lastMtime, 0)}, update, del, nil 189 } 190 191 func (fc *fileCache) sendEvent(item *model.ConfigFileRelease) { 192 err := eventhub.Publish(eventhub.ConfigFilePublishTopic, &eventhub.PublishConfigFileEvent{ 193 Message: item.SimpleConfigFileRelease, 194 }) 195 if err != nil { 196 configLog.Error("[Config][Release][Cache] notify config release change", 197 zap.Any("info", item.ConfigFileReleaseKey), zap.Error(err)) 198 } 199 } 200 201 // handleUpdateRelease 202 func (fc *fileCache) handleUpdateRelease(oldVal *model.SimpleConfigFileRelease, item *model.ConfigFileRelease) error { 203 fc.releases.Put(item.Id, item.SimpleConfigFileRelease) 204 205 func() { 206 // 记录 namespace -> group -> file_name -> []SimpleRelease 信息 207 if _, ok := fc.name2release.Load(item.Namespace); !ok { 208 fc.name2release.Store(item.Namespace, utils.NewSyncMap[string, 209 *utils.SyncMap[string, *utils.SyncMap[string, *model.SimpleConfigFileRelease]]]()) 210 } 211 namespace, _ := fc.name2release.Load(item.Namespace) 212 if _, ok := namespace.Load(item.Group); !ok { 213 namespace.Store(item.Group, utils.NewSyncMap[string, *utils.SyncMap[string, *model.SimpleConfigFileRelease]]()) 214 } 215 group, _ := namespace.Load(item.Group) 216 group.ComputeIfAbsent(item.FileName, func(k string) *utils.SyncMap[string, *model.SimpleConfigFileRelease] { 217 return utils.NewSyncMap[string, *model.SimpleConfigFileRelease]() 218 }) 219 files, _ := group.Load(item.FileName) 220 files.Store(item.Name, item.SimpleConfigFileRelease) 221 }() 222 223 if !item.Active { 224 return nil 225 } 226 227 func() { 228 // 保存 active 状态的所有发布 release 信息 229 if _, ok := fc.activeReleases.Load(item.Namespace); !ok { 230 fc.activeReleases.Store(item.Namespace, utils.NewSyncMap[string, 231 *utils.SyncMap[string, *model.SimpleConfigFileRelease]]()) 232 } 233 namespace, _ := fc.activeReleases.Load(item.Namespace) 234 if _, ok := namespace.Load(item.Group); !ok { 235 namespace.Store(item.Group, utils.NewSyncMap[string, *model.SimpleConfigFileRelease]()) 236 } 237 group, _ := namespace.Load(item.Group) 238 group.Store(item.ActiveKey(), item.SimpleConfigFileRelease) 239 }() 240 241 if err := fc.valueCache.Update(func(tx *bbolt.Tx) error { 242 bucket, err := tx.CreateBucketIfNotExists([]byte(item.OwnerKey())) 243 if err != nil { 244 return err 245 } 246 return bucket.Put([]byte(item.ActiveKey()), []byte(item.Content)) 247 }); err != nil { 248 return errors.Join(err, errors.New("persistent config_file content fail")) 249 } 250 return nil 251 } 252 253 // handleDeleteRelease 254 func (fc *fileCache) handleDeleteRelease(oldVal *model.SimpleConfigFileRelease, item *model.ConfigFileRelease) error { 255 fc.releases.Del(item.Id) 256 257 func() { 258 // 记录 namespace -> group -> file_name -> []SimpleRelease 信息 259 if _, ok := fc.name2release.Load(item.Namespace); !ok { 260 return 261 } 262 namespace, _ := fc.name2release.Load(item.Namespace) 263 if _, ok := namespace.Load(item.Group); !ok { 264 return 265 } 266 group, _ := namespace.Load(item.Group) 267 if _, ok := group.Load(item.FileName); !ok { 268 return 269 } 270 271 files, _ := group.Load(item.FileName) 272 files.Delete(item.Name) 273 274 if files.Len() == 0 { 275 group.Delete(item.FileName) 276 } 277 }() 278 279 if oldVal == nil { 280 return nil 281 } 282 if !oldVal.Active { 283 return nil 284 } 285 if namespace, ok := fc.activeReleases.Load(item.Namespace); ok { 286 if group, ok := namespace.Load(item.Group); ok { 287 group.Delete(item.ActiveKey()) 288 } 289 } 290 if err := fc.valueCache.Update(func(tx *bbolt.Tx) error { 291 bucket := tx.Bucket([]byte(item.OwnerKey())) 292 if bucket == nil { 293 return nil 294 } 295 return bucket.Delete([]byte(item.ActiveKey())) 296 }); err != nil { 297 return errors.Join(err, errors.New("remove config_file content fail")) 298 } 299 return nil 300 } 301 302 // postProcessUpdatedRelease 303 func (fc *fileCache) postProcessUpdatedRelease(affect map[string]map[string]struct{}) { 304 for ns, groups := range affect { 305 nsBucket, ok := fc.name2release.Load(ns) 306 if !ok { 307 continue 308 } 309 if _, ok := fc.metricsReleaseCount.Load(ns); !ok { 310 fc.metricsReleaseCount.Store(ns, utils.NewSyncMap[string, uint64]()) 311 } 312 nsMetric, _ := fc.metricsReleaseCount.Load(ns) 313 for group := range groups { 314 groupBucket, ok := nsBucket.Load(group) 315 if !ok { 316 continue 317 } 318 nsMetric.Store(group, uint64(groupBucket.Len())) 319 } 320 } 321 } 322 323 func (fc *fileCache) LastMtime() time.Time { 324 return fc.BaseCache.LastMtime(fc.Name()) 325 } 326 327 // Clear 328 func (fc *fileCache) Clear() error { 329 return nil 330 } 331 332 func (fc *fileCache) Close() error { 333 if fc.valueCache != nil { 334 if err := fc.valueCache.Close(); err != nil { 335 return err 336 } 337 } 338 return nil 339 } 340 341 // name 342 func (fc *fileCache) Name() string { 343 return types.ConfigFileCacheName 344 } 345 346 // GetActiveRelease 347 func (fc *fileCache) GetGroupActiveReleases(namespace, group string) ([]*model.ConfigFileRelease, string) { 348 nsBucket, ok := fc.activeReleases.Load(namespace) 349 if !ok { 350 return nil, "" 351 } 352 groupBucket, ok := nsBucket.Load(group) 353 if !ok { 354 return nil, "" 355 } 356 ret := make([]*model.ConfigFileRelease, 0, 8) 357 groupBucket.Range(func(key string, val *model.SimpleConfigFileRelease) bool { 358 ret = append(ret, &model.ConfigFileRelease{ 359 SimpleConfigFileRelease: val, 360 }) 361 return true 362 }) 363 groupRevisions, ok := fc.activeReleaseRevisions.Load(namespace) 364 if !ok { 365 return ret, utils.NewUUID() 366 } 367 revision, _ := groupRevisions.Load(group) 368 return ret, revision 369 } 370 371 // GetActiveRelease 372 func (fc *fileCache) GetActiveRelease(namespace, group, fileName string) *model.ConfigFileRelease { 373 nsBucket, ok := fc.activeReleases.Load(namespace) 374 if !ok { 375 return nil 376 } 377 groupBucket, ok := nsBucket.Load(group) 378 if !ok { 379 return nil 380 } 381 searchKey := &model.ConfigFileReleaseKey{ 382 Namespace: namespace, 383 Group: group, 384 FileName: fileName, 385 } 386 simple, ok := groupBucket.Load(searchKey.ActiveKey()) 387 if !ok { 388 return nil 389 } 390 ret := &model.ConfigFileRelease{ 391 SimpleConfigFileRelease: simple, 392 } 393 fc.loadValueCache(ret) 394 return ret 395 } 396 397 // GetRelease 398 func (fc *fileCache) GetRelease(key model.ConfigFileReleaseKey) *model.ConfigFileRelease { 399 var ( 400 simple *model.SimpleConfigFileRelease 401 ) 402 if key.Id != 0 { 403 simple, _ = fc.releases.Get(key.Id) 404 } else { 405 nsB, ok := fc.name2release.Load(key.Namespace) 406 if !ok { 407 return nil 408 } 409 groupB, ok := nsB.Load(key.Group) 410 if !ok { 411 return nil 412 } 413 fileB, ok := groupB.Load(key.FileName) 414 if !ok { 415 return nil 416 } 417 simple, _ = fileB.Load(key.Name) 418 } 419 if simple == nil { 420 return nil 421 } 422 ret := &model.ConfigFileRelease{ 423 SimpleConfigFileRelease: simple, 424 } 425 fc.loadValueCache(ret) 426 return ret 427 } 428 429 func (fc *fileCache) QueryReleases(args *types.ConfigReleaseArgs) (uint32, []*model.SimpleConfigFileRelease, error) { 430 if err := fc.Update(); err != nil { 431 return 0, nil, err 432 } 433 434 values := make([]*model.SimpleConfigFileRelease, 0, args.Limit) 435 fc.name2release.Range(func(namespace string, groups *utils.SyncMap[string, *utils.SyncMap[string, 436 *utils.SyncMap[string, *model.SimpleConfigFileRelease]]]) bool { 437 438 if args.Namespace != "" && utils.IsWildNotMatch(namespace, args.Namespace) { 439 return true 440 } 441 groups.Range(func(group string, files *utils.SyncMap[string, *utils.SyncMap[string, 442 *model.SimpleConfigFileRelease]]) bool { 443 444 if args.Group != "" && utils.IsWildNotMatch(group, args.Group) { 445 return true 446 } 447 files.Range(func(fileName string, releases *utils.SyncMap[string, *model.SimpleConfigFileRelease]) bool { 448 if args.FileName != "" && utils.IsWildNotMatch(fileName, args.FileName) { 449 return true 450 } 451 releases.Range(func(releaseName string, item *model.SimpleConfigFileRelease) bool { 452 if args.ReleaseName != "" && utils.IsWildNotMatch(item.Name, args.ReleaseName) { 453 return true 454 } 455 if args.OnlyActive && !item.Active { 456 return true 457 } 458 if len(args.Metadata) > 0 { 459 for k, v := range args.Metadata { 460 sv := item.Metadata[k] 461 if sv != v { 462 return true 463 } 464 } 465 } 466 467 values = append(values, item) 468 return true 469 }) 470 return true 471 }) 472 return true 473 }) 474 return true 475 }) 476 477 sort.Slice(values, func(i, j int) bool { 478 asc := strings.ToLower(args.OrderType) == "asc" 479 if strings.ToLower(args.OrderField) == "name" { 480 return orderByConfigReleaseName(values[i], values[j], asc) 481 } 482 if strings.ToLower(args.OrderField) == "mtime" { 483 return orderByConfigReleaseMtime(values[i], values[j], asc) 484 } 485 return orderByConfigReleaseVersion(values[i], values[j], asc) 486 }) 487 488 return uint32(len(values)), doPageConfigReleases(values, args), nil 489 } 490 491 func orderByConfigReleaseName(a, b *model.SimpleConfigFileRelease, asc bool) bool { 492 if asc { 493 return a.Name <= b.Name 494 } 495 return a.Name > b.Name 496 } 497 498 func orderByConfigReleaseMtime(a, b *model.SimpleConfigFileRelease, asc bool) bool { 499 if asc { 500 return a.ModifyTime.Before(b.ModifyTime) 501 } 502 return a.ModifyTime.After(b.ModifyTime) 503 } 504 505 func orderByConfigReleaseVersion(a, b *model.SimpleConfigFileRelease, asc bool) bool { 506 if asc { 507 return a.Version < b.Version 508 } 509 return a.Version > b.Version 510 } 511 512 func doPageConfigReleases(values []*model.SimpleConfigFileRelease, 513 args *types.ConfigReleaseArgs) []*model.SimpleConfigFileRelease { 514 515 if args.NoPage { 516 return values 517 } 518 519 offset := args.Offset 520 limit := args.Limit 521 522 amount := uint32(len(values)) 523 if offset >= amount || limit == 0 { 524 return nil 525 } 526 endIdx := offset + limit 527 if endIdx > amount { 528 endIdx = amount 529 } 530 return values[offset:endIdx] 531 } 532 533 func (fc *fileCache) loadValueCache(release *model.ConfigFileRelease) { 534 _ = fc.valueCache.View(func(tx *bbolt.Tx) error { 535 bucket := tx.Bucket([]byte(release.OwnerKey())) 536 if bucket == nil { 537 return nil 538 } 539 val := bucket.Get([]byte(release.ActiveKey())) 540 release.Content = string(val) 541 return nil 542 }) 543 }