github.com/polarismesh/polaris@v1.17.8/cache/service/service_test.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 "math/rand" 23 "reflect" 24 "testing" 25 "time" 26 27 "github.com/golang/mock/gomock" 28 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 29 "github.com/stretchr/testify/assert" 30 31 types "github.com/polarismesh/polaris/cache/api" 32 cachemock "github.com/polarismesh/polaris/cache/mock" 33 "github.com/polarismesh/polaris/common/model" 34 "github.com/polarismesh/polaris/common/utils" 35 "github.com/polarismesh/polaris/store" 36 "github.com/polarismesh/polaris/store/mock" 37 ) 38 39 // 生成一个测试的serviceCache和对应的mock对象 40 func newTestServiceCache(t *testing.T) (*gomock.Controller, *mock.MockStore, *serviceCache, *instanceCache) { 41 ctl := gomock.NewController(t) 42 43 storage := mock.NewMockStore(ctl) 44 mockCacheMgr := cachemock.NewMockCacheManager(ctl) 45 46 mockSvcCache := NewServiceCache(storage, mockCacheMgr) 47 mockInstCache := NewInstanceCache(storage, mockCacheMgr) 48 49 mockCacheMgr.EXPECT().GetCacher(types.CacheService).Return(mockSvcCache).AnyTimes() 50 mockCacheMgr.EXPECT().GetCacher(types.CacheInstance).Return(mockInstCache).AnyTimes() 51 52 mockTx := mock.NewMockTx(ctl) 53 mockTx.EXPECT().Commit().Return(nil).AnyTimes() 54 mockTx.EXPECT().Rollback().Return(nil).AnyTimes() 55 mockTx.EXPECT().CreateReadView().Return(nil).AnyTimes() 56 storage.EXPECT().StartReadTx().Return(mockTx, nil).AnyTimes() 57 storage.EXPECT().GetUnixSecond(gomock.Any()).AnyTimes().Return(time.Now().Unix(), nil) 58 59 opt := map[string]interface{}{ 60 "disableBusiness": false, 61 "needMeta": true, 62 } 63 _ = mockInstCache.Initialize(opt) 64 _ = mockSvcCache.Initialize(opt) 65 66 return ctl, storage, mockSvcCache.(*serviceCache), mockInstCache.(*instanceCache) 67 } 68 69 // 获取当前缓存中的services总数 70 func getServiceCacheCount(sc *serviceCache) int { 71 sum := 0 72 _ = sc.IteratorServices(func(key string, value *model.Service) (bool, error) { 73 sum++ 74 return true, nil 75 }) 76 return sum 77 } 78 79 // 生成一些测试的services 80 func genModelService(total int) map[string]*model.Service { 81 out := make(map[string]*model.Service) 82 for i := 0; i < total; i++ { 83 item := &model.Service{ 84 ID: fmt.Sprintf("ID-%d", i), 85 Namespace: fmt.Sprintf("Namespace-%d", i), 86 Name: fmt.Sprintf("Name-%d", i), 87 Revision: utils.NewUUID(), 88 Valid: true, 89 ModifyTime: time.Unix(int64(i), 0), 90 } 91 out[item.ID] = item 92 } 93 94 return out 95 } 96 97 // 生成一些测试的services 98 func genModelServiceByNamespace(total int, namespace string) map[string]*model.Service { 99 out := make(map[string]*model.Service) 100 for i := 0; i < total; i++ { 101 item := &model.Service{ 102 ID: fmt.Sprintf("%s-ID-%d", namespace, i), 103 Namespace: namespace, 104 Name: fmt.Sprintf("Name-%d", i), 105 Valid: true, 106 Revision: utils.NewUUID(), 107 ModifyTime: time.Unix(int64(i), 0), 108 } 109 out[item.ID] = item 110 } 111 112 return out 113 } 114 115 func genModelInstancesByServicesWithInsId( 116 services map[string]*model.Service, instCount int, insIdPrefix string) (map[string][]*model.Instance, map[string]*model.Instance) { 117 var svcToInstances = make(map[string][]*model.Instance, len(services)) 118 var allInstances = make(map[string]*model.Instance, len(services)*instCount) 119 var idx int 120 for id, svc := range services { 121 label := svc.Name 122 instancesSvc := make([]*model.Instance, 0, instCount) 123 for i := 0; i < instCount; i++ { 124 entry := &model.Instance{ 125 Proto: &apiservice.Instance{ 126 Id: utils.NewStringValue(fmt.Sprintf("%s-instanceID-%s-%d", insIdPrefix, label, idx)), 127 Host: utils.NewStringValue(fmt.Sprintf("host-%s-%d", label, idx)), 128 Port: utils.NewUInt32Value(uint32(idx + 10)), 129 }, 130 ServiceID: svc.ID, 131 Valid: true, 132 } 133 idx++ 134 instancesSvc = append(instancesSvc, entry) 135 allInstances[entry.ID()] = entry 136 } 137 svcToInstances[id] = instancesSvc 138 } 139 return svcToInstances, allInstances 140 } 141 142 // 生成一些测试的services 143 func genModelServiceByNamespaces(total int, namespace []string) map[string]*model.Service { 144 out := make(map[string]*model.Service) 145 for i := 0; i < total; i++ { 146 item := &model.Service{ 147 ID: fmt.Sprintf("ID-%d", i), 148 Namespace: namespace[rand.Intn(len(namespace))], 149 Name: fmt.Sprintf("Name-%d", i), 150 Valid: true, 151 ModifyTime: time.Unix(int64(i), 0), 152 } 153 out[item.ID] = item 154 } 155 156 return out 157 } 158 159 // TestServiceUpdate 测试缓存更新函数 160 func TestServiceUpdate(t *testing.T) { 161 ctl, storage, sc, _ := newTestServiceCache(t) 162 defer ctl.Finish() 163 164 t.Run("所有数据为空, 可以正常获取数据", func(t *testing.T) { 165 gomock.InOrder( 166 storage.EXPECT(). 167 GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta). 168 Return(nil, nil).Times(1), 169 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(0), nil), 170 ) 171 172 if err := sc.Update(); err != nil { 173 t.Fatalf("error: %s", err.Error()) 174 } 175 176 if sum := getServiceCacheCount(sc); sum != 0 { 177 t.Fatalf("error: %d", sum) 178 } 179 }) 180 t.Run("有数据更新, 数据正常", func(t *testing.T) { 181 _ = sc.Clear() 182 services := genModelService(100) 183 gomock.InOrder( 184 storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta). 185 Return(services, nil), 186 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services)), nil), 187 ) 188 189 if err := sc.Update(); err != nil { 190 t.Fatalf("error: %s", err.Error()) 191 } 192 193 if sum := getServiceCacheCount(sc); sum != 100 { 194 t.Fatalf("error: %d", sum) 195 } 196 }) 197 t.Run("有数据更新, 重复更新, 数据更新正常", func(t *testing.T) { 198 _ = sc.Clear() 199 services1 := genModelService(100) 200 services2 := genModelService(300) 201 gomock.InOrder( 202 storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta). 203 Return(services1, nil), 204 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services1)), nil), 205 ) 206 207 if err := sc.Update(); err != nil { 208 t.Fatalf("error: %s", err.Error()) 209 } 210 211 gomock.InOrder( 212 storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta). 213 Return(services2, nil), 214 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services2)), nil), 215 ) 216 _ = sc.Update() 217 if sum := getServiceCacheCount(sc); sum != 300 { 218 t.Fatalf("error: %d", sum) 219 } 220 }) 221 } 222 223 // TestServiceUpdate1 测试缓存更新函数1 224 func TestServiceUpdate1(t *testing.T) { 225 ctl, storage, sc, _ := newTestServiceCache(t) 226 defer ctl.Finish() 227 228 t.Run("服务全部被删除, 会被清除掉", func(t *testing.T) { 229 _ = sc.Clear() 230 services := genModelService(100) 231 gomock.InOrder(storage.EXPECT(). 232 GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil), 233 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(100), nil), 234 ) 235 _ = sc.Update() 236 237 // 把所有的都置为false 238 for _, service := range services { 239 service.Valid = false 240 } 241 242 gomock.InOrder(storage.EXPECT(). 243 GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil), 244 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(0), nil), 245 ) 246 _ = sc.Update() 247 248 if sum := getServiceCacheCount(sc); sum != 0 { 249 t.Fatalf("error: %d", sum) 250 } 251 }) 252 253 t.Run("服务部分被删除, 缓存内容正常", func(t *testing.T) { 254 _ = sc.Clear() 255 services := genModelService(100) 256 gomock.InOrder(storage.EXPECT(). 257 GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil), 258 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(len(services)), nil), 259 ) 260 _ = sc.Update() 261 262 // 把所有的都置为false 263 count := len(services) 264 idx := 0 265 for _, service := range services { 266 if idx%2 == 0 { 267 service.Valid = false 268 count-- 269 } 270 idx++ 271 } 272 273 gomock.InOrder(storage.EXPECT(). 274 GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta).Return(services, nil), 275 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(count), nil), 276 ) 277 _ = sc.Update() 278 279 if sum := getServiceCacheCount(sc); sum != 50 { // remain half 280 t.Fatalf("error: %d", sum) 281 } 282 }) 283 } 284 285 // TestServiceUpdate2 测试缓存更新 286 func TestServiceUpdate2(t *testing.T) { 287 ctl, storage, sc, _ := newTestServiceCache(t) 288 defer ctl.Finish() 289 290 t.Run("store返回失败, update会返回失败", func(t *testing.T) { 291 _ = sc.Clear() 292 gomock.InOrder( 293 storage.EXPECT().GetMoreServices(gomock.Any(), sc.IsFirstUpdate(), sc.disableBusiness, sc.needMeta). 294 Return(nil, fmt.Errorf("store error")), 295 storage.EXPECT().GetServicesCount().AnyTimes().Return(uint32(0), nil), 296 ) 297 298 if err := sc.Update(); err != nil { 299 t.Logf("pass: %s", err.Error()) 300 } else { 301 t.Fatalf("error") 302 } 303 }) 304 } 305 306 // TestGetServiceByName 根据服务名获取服务缓存信息 307 func TestGetServiceByName(t *testing.T) { 308 ctl, _, sc, _ := newTestServiceCache(t) 309 defer ctl.Finish() 310 t.Run("可以根据服务名和命名空间, 正常获取缓存服务信息", func(t *testing.T) { 311 _ = sc.Clear() 312 services := genModelService(20) 313 sc.setServices(services) 314 315 for _, entry := range services { 316 service := sc.GetServiceByName(entry.Name, entry.Namespace) 317 if service == nil { 318 t.Fatalf("error") 319 } 320 } 321 }) 322 t.Run("服务不存在, 返回为空", func(t *testing.T) { 323 _ = sc.Clear() 324 services := genModelService(20) 325 sc.setServices(services) 326 if service := sc.GetServiceByName("aaa", "bbb"); service != nil { 327 t.Fatalf("error") 328 } 329 }) 330 } 331 332 // TestServiceCache_GetServiceByID 根据服务ID获取服务缓存信息 333 func TestServiceCache_GetServiceByID(t *testing.T) { 334 ctl, _, sc, _ := newTestServiceCache(t) 335 defer ctl.Finish() 336 337 t.Run("可以根据服务ID, 正常获取缓存的服务信息", func(t *testing.T) { 338 _ = sc.Clear() 339 services := genModelService(30) 340 sc.setServices(services) 341 342 for _, entry := range services { 343 service := sc.GetServiceByID(entry.ID) 344 if service == nil { 345 t.Fatalf("error") 346 } 347 } 348 }) 349 350 t.Run("缓存内容为空, 根据ID获取数据, 会返回为空", func(t *testing.T) { 351 _ = sc.Clear() 352 services := genModelService(30) 353 sc.setServices(services) 354 355 if service := sc.GetServiceByID("123456789"); service != nil { 356 t.Fatalf("error") 357 } 358 }) 359 } 360 361 func genModelInstancesByServices( 362 services map[string]*model.Service, instCount int) (map[string][]*model.Instance, map[string]*model.Instance) { 363 var svcToInstances = make(map[string][]*model.Instance, len(services)) 364 var allInstances = make(map[string]*model.Instance, len(services)*instCount) 365 var idx int 366 for id, svc := range services { 367 label := svc.Name 368 instancesSvc := make([]*model.Instance, 0, instCount) 369 for i := 0; i < instCount; i++ { 370 entry := &model.Instance{ 371 Proto: &apiservice.Instance{ 372 Id: utils.NewStringValue(fmt.Sprintf("instanceID-%s-%d", label, idx)), 373 Host: utils.NewStringValue(fmt.Sprintf("host-%s-%d", label, idx)), 374 Port: utils.NewUInt32Value(uint32(idx + 10)), 375 }, 376 ServiceID: svc.ID, 377 Valid: true, 378 } 379 idx++ 380 instancesSvc = append(instancesSvc, entry) 381 allInstances[entry.ID()] = entry 382 } 383 svcToInstances[id] = instancesSvc 384 } 385 return svcToInstances, allInstances 386 } 387 388 // TestServiceCache_GetServicesByFilter 根据实例的host查询对应的服务列表 389 func TestServiceCache_GetServicesByFilter(t *testing.T) { 390 ctl, mockStore, sc, _ := newTestServiceCache(t) 391 defer ctl.Finish() 392 393 t.Run("可以根据服务host-正常获取缓存的服务信息", func(t *testing.T) { 394 _ = sc.Clear() 395 services := genModelServiceByNamespace(100, "default") 396 sc.setServices(services) 397 398 svcInstances, instances := genModelInstancesByServices(services, 2) 399 ic := sc.instCache.(*instanceCache) 400 401 mockStore.EXPECT().GetServicesCount().Return(uint32(len(services)), nil).AnyTimes() 402 mockStore.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(len(instances)), nil).AnyTimes() 403 mockStore.EXPECT().GetMoreServices(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(services, nil).AnyTimes() 404 mockStore.EXPECT().GetMoreInstances(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(instances, nil).AnyTimes() 405 ic.setInstances(instances) 406 407 hostToService := make(map[string]string) 408 for svc, instances := range svcInstances { 409 hostToService[instances[0].Host()] = svc 410 } 411 // 先不带命名空间进行查询 412 for host, svcId := range hostToService { 413 instArgs := &store.InstanceArgs{ 414 Hosts: []string{host}, 415 } 416 svcArgs := &types.ServiceArgs{ 417 EmptyCondition: true, 418 } 419 amount, services, err := sc.GetServicesByFilter(svcArgs, instArgs, 0, 10) 420 if err != nil { 421 t.Fatal(err) 422 } 423 if amount != 1 { 424 t.Fatalf("service count is %d, expect 1", amount) 425 } 426 if len(services) != 1 { 427 t.Fatalf("service count is %d, expect 1", len(services)) 428 } 429 if services[0].ID != svcId { 430 t.Fatalf("service id not match, actual %s, expect %s", services[0].ID, svcId) 431 } 432 } 433 434 }) 435 } 436 437 func TestServiceCache_NamespaceCount(t *testing.T) { 438 ctl, _, sc, ic := newTestServiceCache(t) 439 defer ctl.Finish() 440 441 t.Run("先刷新instancesCache, 在刷新serviceCache, 计数等待一段时间之后正常", func(t *testing.T) { 442 _ = sc.Clear() 443 _ = ic.Clear() 444 445 ic := sc.instCache.(*instanceCache) 446 447 nsList := []string{"default", "test-1", "test-2", "test-3"} 448 449 services := genModelServiceByNamespaces(50, nsList) 450 svcInstances, instances := genModelInstancesByServices(services, 2) 451 expectNsCount := make(map[string]int) 452 453 for svcId, instances := range svcInstances { 454 svc := services[svcId] 455 if _, ok := expectNsCount[svc.Namespace]; !ok { 456 expectNsCount[svc.Namespace] = 0 457 } 458 expectNsCount[svc.Namespace] += len(instances) 459 } 460 461 ic.setInstances(instances) 462 time.Sleep(time.Duration(5 * time.Second)) 463 sc.setServices(services) 464 time.Sleep(time.Duration(5 * time.Second)) 465 466 acutalNsCount := make(map[string]int) 467 for i := range nsList { 468 ns := nsList[i] 469 acutalNsCount[ns] = int(sc.GetNamespaceCntInfo(ns).InstanceCnt.TotalInstanceCount) 470 } 471 472 fmt.Printf("expect ns count : %#v\n", expectNsCount) 473 fmt.Printf("acutal ns count : %#v\n", acutalNsCount) 474 475 if !reflect.DeepEqual(expectNsCount, acutalNsCount) { 476 t.Fatal("namespace count is no currect") 477 } 478 }) 479 } 480 481 // TestRevisionWorker 测试revision的管道是否正常 482 func TestRevisionWorker(t *testing.T) { 483 ctl := gomock.NewController(t) 484 storage := mock.NewMockStore(ctl) 485 mockCacheMgr := cachemock.NewMockCacheManager(ctl) 486 storage.EXPECT().GetUnixSecond(gomock.Any()).AnyTimes().Return(time.Now().Unix(), nil) 487 defer ctl.Finish() 488 489 t.Run("revision计算, chan可以正常收发", func(t *testing.T) { 490 svcCache := NewServiceCache(storage, mockCacheMgr) 491 mockInstCache := NewInstanceCache(storage, mockCacheMgr) 492 mockCacheMgr.EXPECT().GetCacher(types.CacheInstance).Return(mockInstCache).AnyTimes() 493 mockCacheMgr.EXPECT().GetCacher(types.CacheService).Return(svcCache).AnyTimes() 494 _ = svcCache.Initialize(map[string]interface{}{}) 495 _ = mockInstCache.Initialize(map[string]interface{}{}) 496 497 t.Cleanup(func() { 498 _ = mockInstCache.Clear() 499 _ = svcCache.Clear() 500 }) 501 502 // mock一下cache中服务的数据 503 maxTotal := 20480 504 services := make(map[string]*model.Service) 505 for i := 0; i < maxTotal; i++ { 506 item := &model.Service{ 507 ID: fmt.Sprintf("service-id-%d", i), 508 Revision: fmt.Sprintf("revision-%d", i), 509 Valid: true, 510 } 511 services[item.ID] = item 512 } 513 storage.EXPECT().GetServicesCount().Return(uint32(maxTotal), nil).AnyTimes() 514 storage.EXPECT().GetMoreServices(gomock.Any(), true, false, false).Return(services, nil) 515 // 触发计算 516 _ = svcCache.Update() 517 time.Sleep(time.Second * 10) 518 assert.Equal(t, maxTotal, svcCache.GetRevisionWorker().GetServiceRevisionCount()) 519 520 services = make(map[string]*model.Service) 521 for i := 0; i < maxTotal; i++ { 522 if i%2 == 0 { 523 item := &model.Service{ 524 ID: fmt.Sprintf("service-id-%d", i), 525 Revision: fmt.Sprintf("revision-%d", i), 526 Valid: false, 527 } 528 services[item.ID] = item 529 } 530 } 531 storage.EXPECT().GetServicesCount().Return(uint32(maxTotal), nil).AnyTimes() 532 storage.EXPECT().GetMoreServices(gomock.Any(), false, false, false).Return(services, nil) 533 // 触发计算 534 _ = svcCache.Update() 535 time.Sleep(time.Second * 20) 536 // 检查是否有正常计算 537 assert.Equal(t, maxTotal/2, svcCache.GetRevisionWorker().GetServiceRevisionCount()) 538 }) 539 } 540 541 // TestComputeRevision 测试计算revision的函数 542 func TestComputeRevision(t *testing.T) { 543 t.Run("instances为空, 可以正常计算", func(t *testing.T) { 544 out, err := ComputeRevision("123", nil) 545 assert.NoError(t, err) 546 assert.NotEmpty(t, out) 547 }) 548 549 t.Run("instances内容一样, 不同顺序, 计算出的revision一样", func(t *testing.T) { 550 instances := make([]*model.Instance, 0, 6) 551 for i := 0; i < 6; i++ { 552 instances = append(instances, &model.Instance{ 553 Proto: &apiservice.Instance{ 554 Revision: utils.NewStringValue(fmt.Sprintf("revision-%d", i)), 555 }, 556 }) 557 } 558 559 lhs, err := ComputeRevision("123", nil) 560 assert.NoError(t, err) 561 assert.NotEmpty(t, lhs) 562 563 // 交换一下数据, 数据内容不变, revision应该保证不变 564 tmp := instances[0] 565 instances[0] = instances[1] 566 instances[1] = instances[3] 567 instances[3] = tmp 568 569 rhs, err := ComputeRevision("123", nil) 570 assert.NoError(t, err) 571 assert.Equal(t, lhs, rhs) 572 }) 573 574 t.Run("serviceRevision发生改变, 返回改变", func(t *testing.T) { 575 lhs, err := ComputeRevision("123", nil) 576 assert.NoError(t, err) 577 assert.NotEmpty(t, lhs) 578 579 rhs, err := ComputeRevision("456", nil) 580 assert.NoError(t, err) 581 assert.NotEqual(t, lhs, rhs) 582 }) 583 584 t.Run("instances内容改变, 返回改变", func(t *testing.T) { 585 instance := &model.Instance{Proto: &apiservice.Instance{Revision: utils.NewStringValue("123456")}} 586 lhs, err := ComputeRevision("123", []*model.Instance{instance}) 587 assert.NoError(t, err) 588 assert.NotEmpty(t, lhs) 589 590 instance.Proto.Revision.Value = "654321" 591 rhs, err := ComputeRevision("456", []*model.Instance{instance}) 592 assert.NoError(t, err) 593 assert.NotEqual(t, lhs, rhs) 594 }) 595 }