github.com/polarismesh/polaris@v1.17.8/cache/service/router_rule.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 service 19 20 import ( 21 "fmt" 22 "sort" 23 "sync" 24 "time" 25 26 apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 27 "go.uber.org/zap" 28 "golang.org/x/sync/singleflight" 29 30 types "github.com/polarismesh/polaris/cache/api" 31 "github.com/polarismesh/polaris/common/model" 32 "github.com/polarismesh/polaris/common/utils" 33 "github.com/polarismesh/polaris/store" 34 ) 35 36 type ( 37 // routingConfigCache Routing rules cache 38 routingConfigCache struct { 39 *types.BaseCache 40 41 serviceCache types.ServiceCache 42 storage store.Store 43 44 bucket *routeRuleBucket 45 46 lastMtimeV1 time.Time 47 lastMtimeV2 time.Time 48 49 singleFlight singleflight.Group 50 51 // pendingV1RuleIds Records need to be converted from V1 to V2 routing rules ID 52 plock sync.Mutex 53 pendingV1RuleIds map[string]*model.RoutingConfig 54 } 55 ) 56 57 // NewRoutingConfigCache Return a object of operating RoutingConfigcache 58 func NewRoutingConfigCache(s store.Store, cacheMgr types.CacheManager) types.RoutingConfigCache { 59 return &routingConfigCache{ 60 BaseCache: types.NewBaseCache(s, cacheMgr), 61 storage: s, 62 } 63 } 64 65 // initialize The function of implementing the cache interface 66 func (rc *routingConfigCache) Initialize(_ map[string]interface{}) error { 67 rc.lastMtimeV1 = time.Unix(0, 0) 68 rc.lastMtimeV2 = time.Unix(0, 0) 69 rc.pendingV1RuleIds = make(map[string]*model.RoutingConfig) 70 rc.bucket = newRouteRuleBucket() 71 rc.serviceCache = rc.BaseCache.CacheMgr.GetCacher(types.CacheService).(*serviceCache) 72 return nil 73 } 74 75 // Update The function of implementing the cache interface 76 func (rc *routingConfigCache) Update() error { 77 // Multiple thread competition, only one thread is updated 78 _, err, _ := rc.singleFlight.Do(rc.Name(), func() (interface{}, error) { 79 return nil, rc.DoCacheUpdate(rc.Name(), rc.realUpdate) 80 }) 81 return err 82 } 83 84 // update The function of implementing the cache interface 85 func (rc *routingConfigCache) realUpdate() (map[string]time.Time, int64, error) { 86 outV1, err := rc.storage.GetRoutingConfigsForCache(rc.LastFetchTime(), rc.IsFirstUpdate()) 87 if err != nil { 88 log.Errorf("[Cache] routing config v1 cache get from store err: %s", err.Error()) 89 return nil, -1, err 90 } 91 92 outV2, err := rc.storage.GetRoutingConfigsV2ForCache(rc.LastFetchTime(), rc.IsFirstUpdate()) 93 if err != nil { 94 log.Errorf("[Cache] routing config v2 cache get from store err: %s", err.Error()) 95 return nil, -1, err 96 } 97 98 lastMtimes := map[string]time.Time{} 99 rc.setRoutingConfigV1(lastMtimes, outV1) 100 rc.setRoutingConfigV2(lastMtimes, outV2) 101 return lastMtimes, int64(len(outV1) + len(outV2)), err 102 } 103 104 // Clear The function of implementing the cache interface 105 func (rc *routingConfigCache) Clear() error { 106 rc.BaseCache.Clear() 107 rc.pendingV1RuleIds = make(map[string]*model.RoutingConfig) 108 rc.bucket = newRouteRuleBucket() 109 rc.lastMtimeV1 = time.Unix(0, 0) 110 rc.lastMtimeV2 = time.Unix(0, 0) 111 return nil 112 } 113 114 // Name The function of implementing the cache interface 115 func (rc *routingConfigCache) Name() string { 116 return types.RoutingConfigName 117 } 118 119 func (rc *routingConfigCache) ListRouterRule(service, namespace string) []*model.ExtendRouterConfig { 120 routerRules := rc.bucket.listEnableRules(service, namespace) 121 ret := make([]*model.ExtendRouterConfig, 0, len(routerRules)) 122 for level := range routerRules { 123 items := routerRules[level] 124 ret = append(ret, items...) 125 } 126 return ret 127 } 128 129 // GetRouterConfigV2 Obtain routing configuration based on serviceid 130 func (rc *routingConfigCache) GetRouterConfigV2(id, service, namespace string) (*apitraffic.Routing, error) { 131 if id == "" && service == "" && namespace == "" { 132 return nil, nil 133 } 134 135 routerRules := rc.bucket.listEnableRules(service, namespace) 136 revisions := make([]string, 0, 8) 137 rulesV2 := make([]*apitraffic.RouteRule, 0, len(routerRules)) 138 for level := range routerRules { 139 items := routerRules[level] 140 for i := range items { 141 entry, err := items[i].ToApi() 142 if err != nil { 143 return nil, err 144 } 145 rulesV2 = append(rulesV2, entry) 146 revisions = append(revisions, entry.GetRevision()) 147 } 148 } 149 revision, err := types.CompositeComputeRevision(revisions) 150 if err != nil { 151 log.Warn("[Cache][Routing] v2=>v1 compute revisions fail, use fake revision", zap.Error(err)) 152 revision = utils.NewV2Revision() 153 } 154 155 resp := &apitraffic.Routing{ 156 Namespace: utils.NewStringValue(namespace), 157 Service: utils.NewStringValue(service), 158 Rules: rulesV2, 159 Revision: utils.NewStringValue(revision), 160 } 161 return resp, nil 162 } 163 164 // GetRouterConfig Obtain routing configuration based on serviceid 165 func (rc *routingConfigCache) GetRouterConfig(id, service, namespace string) (*apitraffic.Routing, error) { 166 if id == "" && service == "" && namespace == "" { 167 return nil, nil 168 } 169 170 routerRules := rc.bucket.listEnableRules(service, namespace) 171 inBounds, outBounds, revisions := rc.convertV2toV1(routerRules, service, namespace) 172 revision, err := types.CompositeComputeRevision(revisions) 173 if err != nil { 174 log.Warn("[Cache][Routing] v2=>v1 compute revisions fail, use fake revision", zap.Error(err)) 175 revision = utils.NewV2Revision() 176 } 177 178 resp := &apitraffic.Routing{ 179 Namespace: utils.NewStringValue(namespace), 180 Service: utils.NewStringValue(service), 181 Inbounds: inBounds, 182 Outbounds: outBounds, 183 Revision: utils.NewStringValue(revision), 184 } 185 186 return formatRoutingResponseV1(resp), nil 187 } 188 189 // formatRoutingResponseV1 Give the client's cache, no need to expose EXTENDINFO information data 190 func formatRoutingResponseV1(ret *apitraffic.Routing) *apitraffic.Routing { 191 inBounds := ret.Inbounds 192 outBounds := ret.Outbounds 193 194 for i := range inBounds { 195 inBounds[i].ExtendInfo = nil 196 } 197 198 for i := range outBounds { 199 outBounds[i].ExtendInfo = nil 200 } 201 return ret 202 } 203 204 // IteratorRouterRule 205 func (rc *routingConfigCache) IteratorRouterRule(iterProc types.RouterRuleIterProc) { 206 // need to traverse the Routing cache bucket of V2 here 207 rc.bucket.foreach(iterProc) 208 } 209 210 // GetRoutingConfigCount Get the total number of routing configuration cache 211 func (rc *routingConfigCache) GetRoutingConfigCount() int { 212 return rc.bucket.size() 213 } 214 215 // setRoutingConfigV1 Update the data of the store to the cache and convert to v2 model 216 func (rc *routingConfigCache) setRoutingConfigV1(lastMtimes map[string]time.Time, cs []*model.RoutingConfig) { 217 rc.plock.Lock() 218 defer rc.plock.Unlock() 219 220 if len(cs) == 0 { 221 return 222 } 223 lastMtimeV1 := rc.LastMtime(rc.Name()).Unix() 224 for _, entry := range cs { 225 if entry.ID == "" { 226 continue 227 } 228 if entry.ModifyTime.Unix() > lastMtimeV1 { 229 lastMtimeV1 = entry.ModifyTime.Unix() 230 } 231 if !entry.Valid { 232 // Delete the cache converted to V2 233 rc.bucket.deleteV1(entry.ID) 234 continue 235 } 236 rc.pendingV1RuleIds[entry.ID] = entry 237 } 238 239 for id := range rc.pendingV1RuleIds { 240 entry := rc.pendingV1RuleIds[id] 241 // Save to the new V2 cache 242 ok, v2rule, err := rc.convertV1toV2(entry) 243 if err != nil { 244 log.Warn("[Cache] routing parse v1 => v2 fail, will try again next", 245 zap.String("rule-id", entry.ID), zap.Error(err)) 246 continue 247 } 248 if !ok { 249 log.Warn("[Cache] routing parse v1 => v2 is nil, will try again next", 250 zap.String("rule-id", entry.ID)) 251 continue 252 } 253 if ok && v2rule != nil { 254 delete(rc.pendingV1RuleIds, id) 255 rc.bucket.saveV1(entry, v2rule) 256 } 257 } 258 259 lastMtimes[rc.Name()] = time.Unix(lastMtimeV1, 0) 260 log.Infof("[Cache] convert routing parse v1 => v2 count : %d", rc.bucket.convertV2Size()) 261 } 262 263 // setRoutingConfigV2 Store V2 Router Caches 264 func (rc *routingConfigCache) setRoutingConfigV2(lastMtimes map[string]time.Time, cs []*model.RouterConfig) { 265 if len(cs) == 0 { 266 return 267 } 268 269 lastMtimeV2 := rc.LastMtime(rc.Name() + "v2").Unix() 270 for _, entry := range cs { 271 if entry.ID == "" { 272 continue 273 } 274 if entry.ModifyTime.Unix() > lastMtimeV2 { 275 lastMtimeV2 = entry.ModifyTime.Unix() 276 } 277 if !entry.Valid { 278 rc.bucket.deleteV2(entry.ID) 279 continue 280 } 281 extendEntry, err := entry.ToExpendRoutingConfig() 282 if err != nil { 283 log.Error("[Cache] routing config v2 convert to expend", zap.Error(err)) 284 continue 285 } 286 rc.bucket.saveV2(extendEntry) 287 } 288 lastMtimes[rc.Name()+"v2"] = time.Unix(lastMtimeV2, 0) 289 } 290 291 func (rc *routingConfigCache) IsConvertFromV1(id string) (string, bool) { 292 val, ok := rc.bucket.v1rulesToOld[id] 293 return val, ok 294 } 295 296 func (rc *routingConfigCache) convertV1toV2(rule *model.RoutingConfig) (bool, []*model.ExtendRouterConfig, error) { 297 svc := rc.serviceCache.GetServiceByID(rule.ID) 298 if svc == nil { 299 s, err := rc.storage.GetServiceByID(rule.ID) 300 if err != nil { 301 return false, nil, err 302 } 303 if s == nil { 304 return true, nil, nil 305 } 306 svc = s 307 } 308 if svc.IsAlias() { 309 return false, nil, fmt.Errorf("svc: %+v is alias", svc) 310 } 311 312 in, out, err := model.ConvertRoutingV1ToExtendV2(svc.Name, svc.Namespace, rule) 313 if err != nil { 314 return false, nil, err 315 } 316 317 ret := make([]*model.ExtendRouterConfig, 0, len(in)+len(out)) 318 ret = append(ret, in...) 319 ret = append(ret, out...) 320 321 return true, ret, nil 322 } 323 324 // convertV2toV1 The routing rules of the V2 version are converted to V1 version to return to the client, 325 // which is used to compatible with SDK issuance configuration. 326 func (rc *routingConfigCache) convertV2toV1(entries map[routingLevel][]*model.ExtendRouterConfig, 327 service, namespace string) ([]*apitraffic.Route, []*apitraffic.Route, []string) { 328 level1 := entries[level1RoutingV2] 329 sort.Slice(level1, func(i, j int) bool { 330 return model.CompareRoutingV2(level1[i], level1[j]) 331 }) 332 333 level2 := entries[level2RoutingV2] 334 sort.Slice(level2, func(i, j int) bool { 335 return model.CompareRoutingV2(level2[i], level2[j]) 336 }) 337 338 level3 := entries[level3RoutingV2] 339 sort.Slice(level3, func(i, j int) bool { 340 return model.CompareRoutingV2(level3[i], level3[j]) 341 }) 342 343 level1inRoutes, level1outRoutes, level1Revisions := model.BuildV1RoutesFromV2(service, namespace, level1) 344 level2inRoutes, level2outRoutes, level2Revisions := model.BuildV1RoutesFromV2(service, namespace, level2) 345 level3inRoutes, level3outRoutes, level3Revisions := model.BuildV1RoutesFromV2(service, namespace, level3) 346 347 revisions := make([]string, 0, len(level1Revisions)+len(level2Revisions)+len(level3Revisions)) 348 revisions = append(revisions, level1Revisions...) 349 revisions = append(revisions, level2Revisions...) 350 revisions = append(revisions, level3Revisions...) 351 352 inRoutes := make([]*apitraffic.Route, 0, len(level1inRoutes)+len(level2inRoutes)+len(level3inRoutes)) 353 inRoutes = append(inRoutes, level1inRoutes...) 354 inRoutes = append(inRoutes, level2inRoutes...) 355 inRoutes = append(inRoutes, level3inRoutes...) 356 357 outRoutes := make([]*apitraffic.Route, 0, len(level1outRoutes)+len(level2outRoutes)+len(level3outRoutes)) 358 outRoutes = append(outRoutes, level1outRoutes...) 359 outRoutes = append(outRoutes, level2outRoutes...) 360 outRoutes = append(outRoutes, level3outRoutes...) 361 362 return inRoutes, outRoutes, revisions 363 }