mirror of
https://github.com/temporalio/temporal.git
synced 2026-08-31 02:51:51 -07:00
## What changed? - Applied testifylint, staticcheck, and gofix auto-fixes. - Exact commands that were run: ```sh .bin/golangci-lint-v2.9.0 run --allow-parallel-runners --concurrency 4 --fix --enable-only testifylint --build-tags disable_grpc_modules,test_dep --timeout 20m --config=.github/.golangci.yml .bin/golangci-lint-v2.9.0 run --allow-parallel-runners --concurrency 4 --fix --enable-only staticcheck --build-tags disable_grpc_modules,test_dep --timeout 20m --config=.github/.golangci.yml make fmt-gofix make goimports make fmt git diff --check ``` - No manual or AI changes were made; except where commented on. - Some fixes caused lint errors; those were reverted again. - Changes were all reviewed by me.
341 lines
12 KiB
Go
341 lines
12 KiB
Go
package searchattribute
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
enumspb "go.temporal.io/api/enums/v1"
|
|
"go.temporal.io/api/serviceerror"
|
|
persistencespb "go.temporal.io/server/api/persistence/v1"
|
|
"go.temporal.io/server/common/clock"
|
|
"go.temporal.io/server/common/persistence"
|
|
"go.temporal.io/server/common/testing/testlogger"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
type (
|
|
searchAttributesManagerSuite struct {
|
|
suite.Suite
|
|
*require.Assertions
|
|
|
|
controller *gomock.Controller
|
|
|
|
logger *testlogger.TestLogger
|
|
timeSource *clock.EventTimeSource
|
|
mockClusterMetadataManager *persistence.MockClusterMetadataManager
|
|
manager *managerImpl
|
|
forceCacheRefresh bool
|
|
}
|
|
)
|
|
|
|
func TestSearchAttributesManagerSuite(t *testing.T) {
|
|
suite.Run(t, &searchAttributesManagerSuite{})
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) SetupSuite() {
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TearDownSuite() {
|
|
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) SetupTest() {
|
|
s.Assertions = require.New(s.T())
|
|
s.controller = gomock.NewController(s.T())
|
|
s.logger = testlogger.NewTestLogger(s.T(), testlogger.FailOnAnyUnexpectedError)
|
|
s.timeSource = clock.NewEventTimeSource()
|
|
s.mockClusterMetadataManager = persistence.NewMockClusterMetadataManager(s.controller)
|
|
s.manager = NewManager(
|
|
s.timeSource,
|
|
s.mockClusterMetadataManager,
|
|
s.logger,
|
|
func() bool {
|
|
return s.forceCacheRefresh
|
|
},
|
|
)
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TearDownTest() {
|
|
s.controller.Finish()
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestGetSearchAttributesCache() {
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 0, 0, time.UTC))
|
|
// Initial call
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
// Second call, no changes in DB (version is the same)
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 10, 0, time.UTC))
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
// Third call, DB changed
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 20, 0, time.UTC))
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 2,
|
|
}, nil)
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(10)
|
|
for goroutine := range 10 {
|
|
go func(goroutine int) {
|
|
defer wg.Done()
|
|
for i := 1; i < 1500; i++ {
|
|
searchAttributes, err := s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Len(searchAttributes.Custom(), 1)
|
|
t, err := searchAttributes.GetType("OrderId")
|
|
s.NoError(err)
|
|
s.Equal(enumspb.INDEXED_VALUE_TYPE_KEYWORD, t)
|
|
if i%500 == 0 && goroutine == 5 {
|
|
// This moves time two times.
|
|
s.timeSource.Update(s.timeSource.Now().Add(cacheRefreshInterval).Add(time.Second))
|
|
}
|
|
}
|
|
}(goroutine)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestGetSearchAttributesCache_Error() {
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 0, 0, time.UTC))
|
|
// Initial call
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(nil, errors.New("random error"))
|
|
s.logger.Expect(testlogger.Error, "failed to refresh search attributes")
|
|
searchAttributes, err := s.manager.GetSearchAttributes("index-name", false)
|
|
s.Error(err)
|
|
s.Empty(searchAttributes.Custom())
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestGetSearchAttributesCache_NotFoundError() {
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 0, 0, time.UTC))
|
|
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(nil, serviceerror.NewNotFound("not found"))
|
|
searchAttributes, err := s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Empty(searchAttributes.Custom())
|
|
|
|
// GetClusterMetadata() shouldn't be called, because results are cached.
|
|
searchAttributes, err = s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Empty(searchAttributes.Custom())
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestGetSearchAttributesCache_UnavailableError() {
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 0, 0, time.UTC))
|
|
|
|
// First call: DB is down, cache is cold
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(nil, serviceerror.NewUnavailable("db is down"))
|
|
s.logger.Expect(testlogger.Error, "failed to refresh search attributes")
|
|
searchAttributes, err := s.manager.GetSearchAttributes("index-name", false)
|
|
s.Error(err)
|
|
s.Empty(searchAttributes.Custom())
|
|
|
|
// Move time forward
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 1, 0, 0, time.UTC))
|
|
|
|
// Second call populates cache.
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
searchAttributes, err = s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Len(searchAttributes.Custom(), 1)
|
|
|
|
// Expire cache.
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 2, 0, 0, 0, time.UTC))
|
|
|
|
// Third call, cache is expired, DB is down, but cache data is returned.
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(nil, serviceerror.NewUnavailable("db is down"))
|
|
searchAttributes, err = s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Len(searchAttributes.Custom(), 1)
|
|
|
|
// Next cache refresh in cacheRefreshIfUnavailableInterval.
|
|
c := s.manager.cache.Load().(cache)
|
|
s.Equal(time.Date(2020, 8, 22, 2, 0, 20, 0, time.UTC), c.expireOn)
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestGetSearchAttributesCache_EmptyIndex() {
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
|
|
searchAttributes, err := s.manager.GetSearchAttributes("", false)
|
|
s.NoError(err)
|
|
s.Len(searchAttributes.Custom(), 1)
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestGetSearchAttributesCache_RefreshIfAbsent() {
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 0, 0, time.UTC))
|
|
|
|
// First call populates cache.
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 2,
|
|
}, nil)
|
|
|
|
searchAttributes, err := s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Empty(searchAttributes.Custom())
|
|
|
|
s.timeSource.Update(time.Date(2020, 8, 22, 1, 0, 1, 0, time.UTC))
|
|
|
|
searchAttributes, err = s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Empty(searchAttributes.Custom())
|
|
|
|
s.forceCacheRefresh = true
|
|
searchAttributes, err = s.manager.GetSearchAttributes("index-name", false)
|
|
s.NoError(err)
|
|
s.Len(searchAttributes.Custom(), 1)
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestSaveSearchAttributes_UpdateIndex() {
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderIdOld": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
|
|
s.mockClusterMetadataManager.EXPECT().SaveClusterMetadata(gomock.Any(), &persistence.SaveClusterMetadataRequest{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}).Return(false, nil)
|
|
|
|
err := s.manager.SaveSearchAttributes(context.Background(), "index-name", map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
})
|
|
s.NoError(err)
|
|
}
|
|
func (s *searchAttributesManagerSuite) TestSaveSearchAttributes_NewIndex() {
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name-2": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId2": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
|
|
s.mockClusterMetadataManager.EXPECT().SaveClusterMetadata(gomock.Any(), &persistence.SaveClusterMetadataRequest{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name-2": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId2": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}},
|
|
"index-name": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}).Return(false, nil)
|
|
|
|
err := s.manager.SaveSearchAttributes(context.Background(), "index-name", map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
})
|
|
s.NoError(err)
|
|
}
|
|
|
|
func (s *searchAttributesManagerSuite) TestSaveSearchAttributesCache_EmptyIndex() {
|
|
s.mockClusterMetadataManager.EXPECT().GetCurrentClusterMetadata(gomock.Any()).Return(&persistence.GetClusterMetadataResponse{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name-2": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId2": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}, nil)
|
|
|
|
s.mockClusterMetadataManager.EXPECT().SaveClusterMetadata(gomock.Any(), &persistence.SaveClusterMetadataRequest{
|
|
ClusterMetadata: &persistencespb.ClusterMetadata{
|
|
IndexSearchAttributes: map[string]*persistencespb.IndexSearchAttributes{
|
|
"index-name-2": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId2": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}},
|
|
"": {
|
|
CustomSearchAttributes: map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
}}},
|
|
},
|
|
Version: 1,
|
|
}).Return(false, nil)
|
|
|
|
err := s.manager.SaveSearchAttributes(context.Background(), "", map[string]enumspb.IndexedValueType{
|
|
"OrderId": enumspb.INDEXED_VALUE_TYPE_KEYWORD,
|
|
})
|
|
s.NoError(err)
|
|
}
|