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  }