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 }