github.com/polarismesh/polaris@v1.17.8/cache/service/instance_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 "sync/atomic" 23 "testing" 24 "time" 25 26 "github.com/golang/mock/gomock" 27 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 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 // 创建一个测试mock instanceCache 40 func newTestInstanceCache(t *testing.T) (*gomock.Controller, *mock.MockStore, *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 58 storage.EXPECT().GetUnixSecond(gomock.Any()).AnyTimes().Return(time.Now().Unix(), nil) 59 opt := map[string]interface{}{ 60 "disableBusiness": false, 61 "needMeta": true, 62 } 63 64 _ = mockSvcCache.Initialize(opt) 65 _ = mockInstCache.Initialize(opt) 66 67 return ctl, storage, mockInstCache.(*instanceCache) 68 } 69 70 // 生成测试数据 71 func genModelInstances(label string, total int) map[string]*model.Instance { 72 out := make(map[string]*model.Instance) 73 for i := 0; i < total; i++ { 74 entry := &model.Instance{ 75 Proto: &apiservice.Instance{ 76 Id: utils.NewStringValue(fmt.Sprintf("instanceID-%s-%d", label, i)), 77 Host: utils.NewStringValue(fmt.Sprintf("host-%s-%d", label, i)), 78 Port: utils.NewUInt32Value(uint32(i + 10)), 79 Location: &apimodel.Location{ 80 Region: utils.NewStringValue("china"), 81 Zone: utils.NewStringValue("ap-shenzheng"), 82 Campus: utils.NewStringValue("ap-shenzheng-1"), 83 }, 84 }, 85 ServiceID: fmt.Sprintf("serviceID-%s", label), 86 Valid: true, 87 } 88 89 out[entry.Proto.Id.GetValue()] = entry 90 } 91 92 return out 93 } 94 95 // 对instanceCache的缓存数据进行计数统计 96 func iteratorInstances(ic *instanceCache) (int, int) { 97 instancesCount := 0 98 services := make(map[string]bool) 99 _ = ic.IteratorInstances(func(key string, value *model.Instance) (b bool, e error) { 100 instancesCount++ 101 if _, ok := services[value.ServiceID]; !ok { 102 services[value.ServiceID] = true 103 } 104 return true, nil 105 }) 106 107 return len(services), instancesCount 108 } 109 110 // TestInstanceCache_Update 测试正常的更新缓存操作 111 func TestInstanceCache_Update(t *testing.T) { 112 ctl, storage, ic := newTestInstanceCache(t) 113 defer ctl.Finish() 114 t.Run("正常更新缓存,缓存数据符合预期", func(t *testing.T) { 115 _ = ic.Clear() 116 ret := make(map[string]*model.Instance) 117 instances1 := genModelInstances("service1", 10) // 每次gen为一个服务的 118 instances2 := genModelInstances("service2", 5) 119 120 for id, instance := range instances1 { 121 ret[id] = instance 122 } 123 for id, instance := range instances2 { 124 ret[id] = instance 125 } 126 127 gomock.InOrder(storage.EXPECT(). 128 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 129 Return(ret, nil)) 130 gomock.InOrder(storage.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(15), nil)) 131 if err := ic.Update(); err != nil { 132 t.Fatalf("error: %s", err.Error()) 133 } 134 135 servicesCount, instancesCount := iteratorInstances(ic) 136 if servicesCount == 2 && instancesCount == 10+5 { // gen两次,有两个不同服务 137 t.Logf("pass") 138 } else { 139 t.Fatalf("error: %d, %d", servicesCount, instancesCount) 140 } 141 }) 142 143 t.Run("数据为空,更新的内容为空", func(t *testing.T) { 144 _ = ic.Clear() 145 gomock.InOrder(storage.EXPECT(). 146 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 147 Return(nil, nil)) 148 if err := ic.Update(); err != nil { 149 t.Fatalf("error: %s", err.Error()) 150 } 151 152 servicesCount, instancesCount := iteratorInstances(ic) 153 if servicesCount != 0 || instancesCount != 0 { 154 t.Fatalf("error: %d %d", servicesCount, instancesCount) 155 } 156 }) 157 158 t.Run("lastMtime可以正常更新", func(t *testing.T) { 159 _ = ic.Clear() 160 instances := genModelInstances("services", 10) 161 maxMtime := time.Now() 162 instances[fmt.Sprintf("instanceID-%s-%d", "services", 5)].ModifyTime = maxMtime 163 164 gomock.InOrder( 165 storage.EXPECT(). 166 GetMoreInstances(gomock.Any(), gomock.Any(), gomock.Any(), ic.needMeta, ic.systemServiceID). 167 Return(instances, nil), 168 storage.EXPECT().GetUnixSecond(gomock.Any()).Return(maxMtime.Unix(), nil).AnyTimes(), 169 ) 170 if err := ic.Update(); err != nil { 171 t.Fatalf("error: %s", err.Error()) 172 } 173 174 if ic.LastMtime().Unix() != maxMtime.Unix() { 175 t.Fatalf("error %d %d", ic.LastMtime().Unix(), maxMtime.Unix()) 176 } 177 }) 178 } 179 180 // TestInstanceCache_Update2 异常场景下的update测试 181 func TestInstanceCache_Update2(t *testing.T) { 182 ctl, storage, ic := newTestInstanceCache(t) 183 defer ctl.Finish() 184 t.Run("数据库返回失败,update会返回失败", func(t *testing.T) { 185 _ = ic.Clear() 186 gomock.InOrder(storage.EXPECT(). 187 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 188 Return(nil, fmt.Errorf("storage get error"))) 189 gomock.InOrder(storage.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(0), fmt.Errorf("storage get error"))) 190 if err := ic.Update(); err != nil { 191 t.Logf("pass: %s", err.Error()) 192 } else { 193 t.Errorf("error") 194 } 195 }) 196 197 t.Run("更新数据,再删除部分数据,缓存正常", func(t *testing.T) { 198 _ = ic.Clear() 199 instances := genModelInstances("service-a", 20) 200 gomock.InOrder(storage.EXPECT(). 201 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 202 Return(instances, nil)) 203 if err := ic.Update(); err != nil { 204 t.Fatalf("error: %s", err.Error()) 205 } 206 207 idx := 0 208 var invalidCount = 0 209 for _, entry := range instances { 210 if idx%2 == 0 { 211 entry.Valid = false 212 invalidCount++ 213 } 214 idx++ 215 } 216 gomock.InOrder(storage.EXPECT(). 217 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 218 Return(instances, nil)) 219 if err := ic.Update(); err != nil { 220 t.Fatalf("error: %s", err.Error()) 221 } 222 223 servicesCount, instancesCount := iteratorInstances(ic) 224 if servicesCount != 1 || instancesCount != 10 { 225 t.Fatalf("error: %d %d", servicesCount, instancesCount) 226 } 227 }) 228 229 t.Run("对账发现缓存数据数量和存储层不一致", func(t *testing.T) { 230 _ = ic.Clear() 231 instances := genModelInstances("service-a", 20) 232 queryCount := int32(0) 233 storage.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(0), nil).AnyTimes() 234 storage.EXPECT(). 235 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 236 DoAndReturn(func(tx store.Tx, mtime time.Time, firstUpdate, needMeta bool, svcIds []string) (map[string]*model.Instance, error) { 237 atomic.AddInt32(&queryCount, 1) 238 if atomic.LoadInt32(&queryCount) == 2 { 239 assert.Equal(t, time.Unix(0, 0), mtime) 240 } 241 return instances, nil 242 }).AnyTimes() 243 244 if err := ic.Update(); err != nil { 245 t.Fatalf("error: %s", err.Error()) 246 } 247 }) 248 } 249 250 // 根据实例ID获取缓存内容 251 func TestInstanceCache_GetInstance(t *testing.T) { 252 ctl, storage, ic := newTestInstanceCache(t) 253 defer ctl.Finish() 254 t.Run("缓存有数据,可以正常获取到数据", func(t *testing.T) { 255 _ = ic.Clear() 256 instances := genModelInstances("my-services", 10) 257 gomock.InOrder(storage.EXPECT(). 258 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 259 Return(instances, nil)) 260 gomock.InOrder(storage.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(10), nil)) 261 if err := ic.Update(); err != nil { 262 t.Fatalf("error: %s", err.Error()) 263 } 264 265 if instance := ic.GetInstance(instances[fmt.Sprintf("instanceID-%s-%d", "my-services", 6)].ID()); instance == nil { 266 t.Fatalf("error") 267 } 268 269 if instance := ic.GetInstance("test-instance-xx"); instance != nil { 270 t.Fatalf("error") 271 } 272 }) 273 } 274 275 func TestInstanceCache_GetServicePorts(t *testing.T) { 276 ctl, storage, ic := newTestInstanceCache(t) 277 defer ctl.Finish() 278 t.Run("缓存有数据,可以正常获取到服务的端口列表", func(t *testing.T) { 279 _ = ic.Clear() 280 instances := genModelInstances("my-services", 10) 281 282 ports := make(map[string][]*model.ServicePort) 283 284 for i := range instances { 285 ins := instances[i] 286 if _, ok := ports[ins.ServiceID]; !ok { 287 ports[ins.ServiceID] = make([]*model.ServicePort, 0, 4) 288 } 289 290 values := ports[ins.ServiceID] 291 find := false 292 293 for j := range values { 294 if values[j].Port == ins.Port() { 295 find = true 296 break 297 } 298 } 299 300 if !find { 301 values = append(values, &model.ServicePort{ 302 Port: ins.Port(), 303 }) 304 } 305 306 ports[ins.ServiceID] = values 307 } 308 309 gomock.InOrder(storage.EXPECT(). 310 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 311 Return(instances, nil)) 312 gomock.InOrder(storage.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(10), nil)) 313 if err := ic.Update(); err != nil { 314 t.Fatalf("error: %s", err.Error()) 315 } 316 317 for i := range instances { 318 ins := instances[i] 319 320 expectVal := ports[ins.ServiceID] 321 targetVal := ic.GetServicePorts(ins.ServiceID) 322 t.Logf("service-ports expectVal : %v, targetVal : %v", expectVal, targetVal) 323 assert.ElementsMatch(t, expectVal, targetVal) 324 } 325 }) 326 } 327 328 func TestInstanceCache_fillIntrnalLabels(t *testing.T) { 329 ctl, storage, ic := newTestInstanceCache(t) 330 defer ctl.Finish() 331 t.Run("向实例Metadata中自动注入北极星默认label信息", func(t *testing.T) { 332 _ = ic.Clear() 333 instances := genModelInstances("inject-internal-label", 10) 334 335 ports := make(map[string][]string) 336 337 for i := range instances { 338 ins := instances[i] 339 if _, ok := ports[ins.ServiceID]; !ok { 340 ports[ins.ServiceID] = make([]string, 0, 4) 341 } 342 343 values := ports[ins.ServiceID] 344 find := false 345 346 for j := range values { 347 if values[j] == fmt.Sprintf("%d", ins.Port()) { 348 find = true 349 break 350 } 351 } 352 353 if !find { 354 values = append(values, fmt.Sprintf("%d", ins.Port())) 355 } 356 357 ports[ins.ServiceID] = values 358 } 359 360 gomock.InOrder(storage.EXPECT(). 361 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 362 Return(instances, nil)) 363 gomock.InOrder(storage.EXPECT().GetInstancesCountTx(gomock.Any()).Return(uint32(10), nil)) 364 if err := ic.Update(); err != nil { 365 t.Fatalf("error: %s", err.Error()) 366 } 367 368 for i := range instances { 369 ins := instances[i] 370 371 assert.Equal(t, map[string]string{ 372 "region": "china", 373 "zone": "ap-shenzheng", 374 "campus": "ap-shenzheng-1", 375 }, ins.Proto.Metadata) 376 } 377 }) 378 } 379 380 const ( 381 instances1Count = 50 382 instances2Count = 30 383 ) 384 385 // TestGetInstancesByServiceID 根据ServiceID获取缓存内容 386 func TestGetInstancesByServiceID(t *testing.T) { 387 ctl, storage, ic := newTestInstanceCache(t) 388 defer ctl.Finish() 389 t.Run("可以通过serviceID获取实例信息", func(t *testing.T) { 390 _ = ic.Clear() 391 instances1 := genModelInstances("my-services", instances1Count) 392 instances2 := genModelInstances("my-services-a", instances2Count) 393 // instances2 = append(instances2, instances1...) 394 395 ret := make(map[string]*model.Instance) 396 for id, instance := range instances1 { 397 ret[id] = instance 398 } 399 for id, instance := range instances2 { 400 ret[id] = instance 401 } 402 403 gomock.InOrder(storage.EXPECT(). 404 GetMoreInstances(gomock.Any(), gomock.Any(), ic.IsFirstUpdate(), ic.needMeta, ic.systemServiceID). 405 Return(ret, nil)) 406 gomock.InOrder(storage.EXPECT(). 407 GetInstancesCountTx(gomock.Any()). 408 Return(uint32(instances1Count+instances2Count), nil)) 409 if err := ic.Update(); err != nil { 410 t.Fatalf("error: %s", err.Error()) 411 } 412 413 key := fmt.Sprintf("instanceID-%s-%d", "my-services-a", 1) 414 if instances := ic.GetInstancesByServiceID(instances2[key].ServiceID); instances != nil { 415 if len(instances) == instances2Count { 416 t.Logf("pass") 417 } else { 418 t.Fatalf("error") 419 } 420 } else { 421 t.Fatalf("error") 422 } 423 424 if instances := ic.GetInstancesByServiceID("aa"); instances != nil { 425 t.Fatalf("error") 426 } 427 }) 428 }