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  }