github.com/polarismesh/polaris@v1.17.8/store/boltdb/routing.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 boltdb
    19  
    20  import (
    21  	"errors"
    22  	"sort"
    23  	"strings"
    24  	"time"
    25  
    26  	bolt "go.etcd.io/bbolt"
    27  
    28  	"github.com/polarismesh/polaris/common/model"
    29  	"github.com/polarismesh/polaris/store"
    30  )
    31  
    32  var _ store.RoutingConfigStore = (*routingStore)(nil)
    33  
    34  type routingStore struct {
    35  	handler BoltHandler
    36  }
    37  
    38  const (
    39  	tblNameRouting         = "routing"
    40  	routingFieldID         = "ID"
    41  	routingFieldInBounds   = "InBounds"
    42  	routingFieldOutBounds  = "OutBounds"
    43  	routingFieldRevision   = "Revision"
    44  	routingFieldModifyTime = "ModifyTime"
    45  	routingFieldValid      = "Valid"
    46  )
    47  
    48  // CreateRoutingConfig Add a routing configuration
    49  func (r *routingStore) CreateRoutingConfig(conf *model.RoutingConfig) error {
    50  	if conf.ID == "" || conf.Revision == "" {
    51  		log.Errorf("[Store][boltdb] create routing config missing service id or revision")
    52  		return store.NewStatusError(store.EmptyParamsErr, "missing service id or revision")
    53  	}
    54  	if conf.InBounds == "" || conf.OutBounds == "" {
    55  		log.Errorf("[Store][boltdb] create routing config missing params")
    56  		return store.NewStatusError(store.EmptyParamsErr, "missing some params")
    57  	}
    58  
    59  	if err := r.cleanRoutingConfig(conf.ID); err != nil {
    60  		return err
    61  	}
    62  
    63  	initRouting(conf)
    64  
    65  	err := r.handler.SaveValue(tblNameRouting, conf.ID, conf)
    66  	if err != nil {
    67  		log.Errorf("add routing config to kv error, %v", err)
    68  		return err
    69  	}
    70  	return nil
    71  }
    72  
    73  // cleanRoutingConfig 从数据库彻底清理路由配置
    74  func (r *routingStore) cleanRoutingConfig(serviceID string) error {
    75  	err := r.handler.DeleteValues(tblNameRouting, []string{serviceID})
    76  	if err != nil {
    77  		log.Errorf("[Store][boltdb] delete invalid route config error, %v", err)
    78  		return err
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // UpdateRoutingConfig Update a routing configuration
    85  func (r *routingStore) UpdateRoutingConfig(conf *model.RoutingConfig) error {
    86  
    87  	if conf.ID == "" || conf.Revision == "" {
    88  		log.Errorf("[Store][boltdb] update routing config missing service id or revision")
    89  		return store.NewStatusError(store.EmptyParamsErr, "missing service id or revision")
    90  	}
    91  	if conf.InBounds == "" || conf.OutBounds == "" {
    92  		log.Errorf("[Store][boltdb] update routing config missing params")
    93  		return store.NewStatusError(store.EmptyParamsErr, "missing some params")
    94  	}
    95  
    96  	properties := make(map[string]interface{})
    97  	properties[routingFieldInBounds] = conf.InBounds
    98  	properties[routingFieldOutBounds] = conf.OutBounds
    99  	properties[routingFieldRevision] = conf.Revision
   100  	properties[routingFieldModifyTime] = time.Now()
   101  
   102  	err := r.handler.UpdateValue(tblNameRouting, conf.ID, properties)
   103  	if err != nil {
   104  		log.Errorf("[Store][boltdb] update route config to kv error, %v", err)
   105  		return err
   106  	}
   107  	return nil
   108  }
   109  
   110  // DeleteRoutingConfig Delete a routing configuration
   111  func (r *routingStore) DeleteRoutingConfig(serviceID string) error {
   112  	if serviceID == "" {
   113  		log.Errorf("[Store][boltdb] delete routing config missing service id")
   114  		return store.NewStatusError(store.EmptyParamsErr, "missing service id")
   115  	}
   116  
   117  	properties := make(map[string]interface{})
   118  	properties[routingFieldValid] = false
   119  	properties[routingFieldModifyTime] = time.Now()
   120  
   121  	err := r.handler.UpdateValue(tblNameRouting, serviceID, properties)
   122  	if err != nil {
   123  		log.Errorf("[Store][boltdb] delete route config to kv error, %v", err)
   124  		return err
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func (r *routingStore) DeleteRoutingConfigTx(tx store.Tx, serviceID string) error {
   131  	if tx == nil {
   132  		return errors.New("tx is nil")
   133  	}
   134  
   135  	if serviceID == "" {
   136  		log.Errorf("[Store][boltdb] delete routing config missing service id")
   137  		return store.NewStatusError(store.EmptyParamsErr, "missing service id")
   138  	}
   139  
   140  	properties := make(map[string]interface{})
   141  	properties[routingFieldValid] = false
   142  	properties[routingFieldModifyTime] = time.Now()
   143  
   144  	boltTx := tx.GetDelegateTx().(*bolt.Tx)
   145  	err := updateValue(boltTx, tblNameRouting, serviceID, properties)
   146  	if err != nil {
   147  		log.Errorf("[Store][boltdb] delete route config to kv error, %v", err)
   148  		return err
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  // GetRoutingConfigsForCache Get incremental routing configuration information through mtime
   155  func (r *routingStore) GetRoutingConfigsForCache(mtime time.Time, firstUpdate bool) ([]*model.RoutingConfig, error) {
   156  
   157  	fields := []string{routingFieldModifyTime}
   158  
   159  	routes, err := r.handler.LoadValuesByFilter(tblNameRouting, fields, &model.RoutingConfig{},
   160  		func(m map[string]interface{}) bool {
   161  			rMtime, ok := m[routingFieldModifyTime]
   162  			if !ok {
   163  				return false
   164  			}
   165  			routeMtime := rMtime.(time.Time)
   166  			return !routeMtime.Before(mtime)
   167  		})
   168  	if err != nil {
   169  		log.Errorf("[Store][boltdb] load route config from kv error, %v", err)
   170  		return nil, err
   171  	}
   172  
   173  	return toRouteConf(routes), nil
   174  }
   175  
   176  // GetRoutingConfigWithService Get routing configuration based on service name and namespace
   177  func (r *routingStore) GetRoutingConfigWithService(name string, namespace string) (*model.RoutingConfig, error) {
   178  
   179  	dbOp := r.handler
   180  	ss := &serviceStore{
   181  		handler: dbOp,
   182  	}
   183  
   184  	// get service first
   185  	service, err := ss.getServiceByNameAndNs(name, namespace)
   186  	if err != nil {
   187  		log.Errorf("[Store][boltdb] get service in route conf error, %v", err)
   188  		return nil, err
   189  	}
   190  
   191  	if service == nil {
   192  		return nil, nil
   193  	}
   194  
   195  	routeC, err := r.getWithID(service.ID)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	return routeC, nil
   200  }
   201  
   202  // GetRoutingConfigWithID Get routing configuration based on service ID
   203  func (r *routingStore) GetRoutingConfigWithID(id string) (*model.RoutingConfig, error) {
   204  	return r.getWithID(id)
   205  }
   206  
   207  func (r *routingStore) getWithID(id string) (*model.RoutingConfig, error) {
   208  	fields := []string{routingFieldID}
   209  	routeConf, err := r.handler.LoadValuesByFilter(tblNameRouting, fields, &model.RoutingConfig{},
   210  		func(m map[string]interface{}) bool {
   211  			return id == m[routingFieldID].(string)
   212  		})
   213  	if err != nil {
   214  		log.Errorf("[Store][boltdb] load route config from kv error, %v", err)
   215  		return nil, err
   216  	}
   217  
   218  	routeC, ok := routeConf[id].(*model.RoutingConfig)
   219  	if !ok {
   220  		return nil, nil
   221  	}
   222  
   223  	if !routeC.Valid {
   224  		return nil, nil
   225  	}
   226  
   227  	return routeC, nil
   228  }
   229  
   230  // GetRoutingConfigs Get routing configuration list
   231  func (r *routingStore) GetRoutingConfigs(
   232  	filter map[string]string, offset uint32, limit uint32) (uint32, []*model.ExtendRoutingConfig, error) {
   233  
   234  	// get all route config
   235  	fields := []string{routingFieldInBounds, routingFieldOutBounds, routingFieldRevision, routingFieldValid}
   236  
   237  	svcName, hasSvcName := filter["name"]
   238  	svcNs, hasSvcNs := filter["namespace"]
   239  	inBounds, isInBounds := filter["inBounds"]
   240  	outBounds, isOutBounds := filter["outBounds"]
   241  	revision, isRevision := filter["revision"]
   242  
   243  	routeConf, err := r.handler.LoadValuesByFilter(tblNameRouting, fields, &model.RoutingConfig{},
   244  		func(m map[string]interface{}) bool {
   245  			if valid, _ := m[routingFieldValid].(bool); !valid {
   246  				return false
   247  			}
   248  
   249  			if isInBounds {
   250  				rIn, ok := m[routingFieldInBounds]
   251  				if !ok {
   252  					return false
   253  				}
   254  				if inBounds != rIn.(string) {
   255  					return false
   256  				}
   257  			}
   258  			if isOutBounds {
   259  				rOut, ok := m[routingFieldOutBounds]
   260  				if !ok {
   261  					return false
   262  				}
   263  				if outBounds != rOut.(string) {
   264  					return false
   265  				}
   266  			}
   267  			if isRevision {
   268  				rRe, ok := m[routingFieldRevision]
   269  				if !ok {
   270  					return false
   271  				}
   272  				if revision != rRe.(string) {
   273  					return false
   274  				}
   275  			}
   276  			return true
   277  		})
   278  	if err != nil {
   279  		log.Errorf("[Store][boltdb] load route config from kv error, %v", err)
   280  		return 0, nil, err
   281  	}
   282  
   283  	if len(routeConf) == 0 {
   284  		return 0, nil, nil
   285  	}
   286  
   287  	// get service
   288  	svcIds := make(map[string]bool)
   289  	for k := range routeConf {
   290  		svcIds[k] = true
   291  	}
   292  
   293  	fields = []string{SvcFieldID, SvcFieldName, SvcFieldNamespace, SvcFieldValid}
   294  
   295  	services, err := r.handler.LoadValuesByFilter(tblNameService, fields, &model.Service{},
   296  		func(m map[string]interface{}) bool {
   297  
   298  			if valid, _ := m[SvcFieldValid].(bool); !valid {
   299  				return false
   300  			}
   301  
   302  			rId, ok := m[SvcFieldID]
   303  			if !ok {
   304  				return false
   305  			}
   306  
   307  			savcName := m[SvcFieldName].(string)
   308  			savcNamespace := m[SvcFieldNamespace].(string)
   309  
   310  			if hasSvcName && savcName != svcName {
   311  				return false
   312  			}
   313  			if hasSvcNs && savcNamespace != svcNs {
   314  				return false
   315  			}
   316  
   317  			id := rId.(string)
   318  			_, ok = svcIds[id]
   319  			return ok
   320  		})
   321  
   322  	out := make([]*model.ExtendRoutingConfig, 0, 4)
   323  
   324  	for id, r := range routeConf {
   325  		var temp model.ExtendRoutingConfig
   326  		svc, ok := services[id].(*model.Service)
   327  		if ok {
   328  			temp.ServiceName = svc.Name
   329  			temp.NamespaceName = svc.Namespace
   330  		} else {
   331  			log.Warnf("[Store][boltdb] get service in route conf error, service is nil, id: %s", id)
   332  			continue
   333  		}
   334  		temp.Config = r.(*model.RoutingConfig)
   335  
   336  		out = append(out, &temp)
   337  	}
   338  
   339  	return uint32(len(routeConf)), getRealRouteConfList(out, offset, limit), nil
   340  }
   341  
   342  func toRouteConf(m map[string]interface{}) []*model.RoutingConfig {
   343  	var routeConf []*model.RoutingConfig
   344  	for _, r := range m {
   345  		routeConf = append(routeConf, r.(*model.RoutingConfig))
   346  	}
   347  
   348  	return routeConf
   349  }
   350  
   351  func getRealRouteConfList(routeConf []*model.ExtendRoutingConfig, offset, limit uint32) []*model.ExtendRoutingConfig {
   352  
   353  	beginIndex := offset
   354  	endIndex := beginIndex + limit
   355  	totalCount := uint32(len(routeConf))
   356  	// handle invalid offset, limit
   357  	if totalCount == 0 {
   358  		return routeConf
   359  	}
   360  	if beginIndex >= endIndex {
   361  		return routeConf
   362  	}
   363  	if beginIndex >= totalCount {
   364  		return routeConf
   365  	}
   366  	if endIndex > totalCount {
   367  		endIndex = totalCount
   368  	}
   369  
   370  	sort.Slice(routeConf, func(i, j int) bool {
   371  		// sort by modify time
   372  		if routeConf[i].Config.ModifyTime.After(routeConf[j].Config.ModifyTime) {
   373  			return true
   374  		} else if routeConf[i].Config.ModifyTime.Before(routeConf[j].Config.ModifyTime) {
   375  			return false
   376  		} else {
   377  			return strings.Compare(routeConf[i].Config.ID, routeConf[j].Config.ID) < 0
   378  		}
   379  	})
   380  
   381  	return routeConf[beginIndex:endIndex]
   382  }
   383  
   384  func initRouting(r *model.RoutingConfig) {
   385  	currTime := time.Now()
   386  	r.CreateTime = currTime
   387  	r.ModifyTime = currTime
   388  	r.Valid = true
   389  }