mirror of
https://github.com/milvus-io/milvus.git
synced 2026-07-21 10:15:43 +00:00
enhance: improve the preformance of create partitions (#47486)
issue: https://github.com/milvus-io/milvus/issues/47403 this pr contains performance optimization for concurrent CreatePartition: 1. add a version cache on proxy for partition level cache 2. avoid repeated updates of target when there're many CreatePartition requests. 3. avoid unnecessary copy of meta data. --------- Signed-off-by: sunby <sunbingyi1992@gmail.com>
This commit is contained in:
@@ -377,6 +377,7 @@ proxy:
|
||||
slowQuerySpanInSeconds: 1 # threshold for slow query detection in seconds. For Search/HybridSearch requests, the time is divided by nq for more accurate per-query measurement. Triggers slow log, WebUI display, and metrics.
|
||||
queryNodePooling:
|
||||
size: 10 # the size for shardleader(querynode) client pool
|
||||
metaCacheGCTimeInterval: 300 # the time interval for meta cache GC, in seconds
|
||||
partialResultRequiredDataRatio: 1 # partial result required data ratio, default to 1 which means disable partial result, otherwise, it will be used as the minimum data ratio for partial result
|
||||
http:
|
||||
enabled: true # Whether to enable the http server
|
||||
|
||||
@@ -491,6 +491,10 @@ func (s *mixCoordImpl) CreatePartition(ctx context.Context, req *milvuspb.Create
|
||||
return s.rootcoordServer.CreatePartition(ctx, req)
|
||||
}
|
||||
|
||||
func (s *mixCoordImpl) CreatePartitionV2(ctx context.Context, req *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
return s.rootcoordServer.CreatePartitionV2(ctx, req)
|
||||
}
|
||||
|
||||
func (s *mixCoordImpl) DropPartition(ctx context.Context, req *milvuspb.DropPartitionRequest) (*commonpb.Status, error) {
|
||||
return s.rootcoordServer.DropPartition(ctx, req)
|
||||
}
|
||||
|
||||
@@ -392,6 +392,10 @@ func (m *mockMixCoord) CreatePartition(ctx context.Context, req *milvuspb.Create
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (m *mockMixCoord) CreatePartitionV2(ctx context.Context, req *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (m *mockMixCoord) DropPartition(ctx context.Context, req *milvuspb.DropPartitionRequest) (*commonpb.Status, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
@@ -394,6 +394,18 @@ func (c *Client) CreatePartition(ctx context.Context, in *milvuspb.CreatePartiti
|
||||
})
|
||||
}
|
||||
|
||||
// CreatePartitionV2 create partition and return partition ID
|
||||
func (c *Client) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
in = typeutil.Clone(in)
|
||||
commonpbutil.UpdateMsgBase(
|
||||
in.GetBase(),
|
||||
commonpbutil.FillMsgBaseFromClient(paramtable.GetNodeID(), commonpbutil.WithTargetID(c.grpcClient.GetNodeID())),
|
||||
)
|
||||
return wrapGrpcCall(ctx, c, func(client MixCoordClient) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
return client.CreatePartitionV2(ctx, in)
|
||||
})
|
||||
}
|
||||
|
||||
// DropPartition drop partition
|
||||
func (c *Client) DropPartition(ctx context.Context, in *milvuspb.DropPartitionRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||
in = typeutil.Clone(in)
|
||||
|
||||
@@ -415,6 +415,10 @@ func (s *Server) CreatePartition(ctx context.Context, in *milvuspb.CreatePartiti
|
||||
return s.mixCoord.CreatePartition(ctx, in)
|
||||
}
|
||||
|
||||
func (s *Server) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
return s.mixCoord.CreatePartitionV2(ctx, in)
|
||||
}
|
||||
|
||||
// DropPartition drops the specified partition.
|
||||
func (s *Server) DropPartition(ctx context.Context, in *milvuspb.DropPartitionRequest) (*commonpb.Status, error) {
|
||||
return s.mixCoord.DropPartition(ctx, in)
|
||||
|
||||
@@ -1875,6 +1875,65 @@ func (_c *MixCoord_CreatePartition_Call) RunAndReturn(run func(context.Context,
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePartitionV2 provides a mock function with given fields: _a0, _a1
|
||||
func (_m *MixCoord) CreatePartitionV2(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreatePartitionV2")
|
||||
}
|
||||
|
||||
var r0 *rootcoordpb.CreatePartitionResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest) *rootcoordpb.CreatePartitionResponse); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*rootcoordpb.CreatePartitionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.CreatePartitionRequest) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MixCoord_CreatePartitionV2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePartitionV2'
|
||||
type MixCoord_CreatePartitionV2_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreatePartitionV2 is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *milvuspb.CreatePartitionRequest
|
||||
func (_e *MixCoord_Expecter) CreatePartitionV2(_a0 interface{}, _a1 interface{}) *MixCoord_CreatePartitionV2_Call {
|
||||
return &MixCoord_CreatePartitionV2_Call{Call: _e.mock.On("CreatePartitionV2", _a0, _a1)}
|
||||
}
|
||||
|
||||
func (_c *MixCoord_CreatePartitionV2_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest)) *MixCoord_CreatePartitionV2_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(*milvuspb.CreatePartitionRequest))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MixCoord_CreatePartitionV2_Call) Return(_a0 *rootcoordpb.CreatePartitionResponse, _a1 error) *MixCoord_CreatePartitionV2_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MixCoord_CreatePartitionV2_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error)) *MixCoord_CreatePartitionV2_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePrivilegeGroup provides a mock function with given fields: _a0, _a1
|
||||
func (_m *MixCoord) CreatePrivilegeGroup(_a0 context.Context, _a1 *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
@@ -2378,6 +2378,80 @@ func (_c *MockMixCoordClient_CreatePartition_Call) RunAndReturn(run func(context
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePartitionV2 provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *MockMixCoordClient) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreatePartitionV2")
|
||||
}
|
||||
|
||||
var r0 *rootcoordpb.CreatePartitionResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error)); ok {
|
||||
return rf(ctx, in, opts...)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) *rootcoordpb.CreatePartitionResponse); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*rootcoordpb.CreatePartitionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockMixCoordClient_CreatePartitionV2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePartitionV2'
|
||||
type MockMixCoordClient_CreatePartitionV2_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreatePartitionV2 is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - in *milvuspb.CreatePartitionRequest
|
||||
// - opts ...grpc.CallOption
|
||||
func (_e *MockMixCoordClient_Expecter) CreatePartitionV2(ctx interface{}, in interface{}, opts ...interface{}) *MockMixCoordClient_CreatePartitionV2_Call {
|
||||
return &MockMixCoordClient_CreatePartitionV2_Call{Call: _e.mock.On("CreatePartitionV2",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *MockMixCoordClient_CreatePartitionV2_Call) Run(run func(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption)) *MockMixCoordClient_CreatePartitionV2_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*milvuspb.CreatePartitionRequest), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockMixCoordClient_CreatePartitionV2_Call) Return(_a0 *rootcoordpb.CreatePartitionResponse, _a1 error) *MockMixCoordClient_CreatePartitionV2_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockMixCoordClient_CreatePartitionV2_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error)) *MockMixCoordClient_CreatePartitionV2_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePrivilegeGroup provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *MockMixCoordClient) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
|
||||
@@ -1216,6 +1216,65 @@ func (_c *MockRootCoord_CreatePartition_Call) RunAndReturn(run func(context.Cont
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePartitionV2 provides a mock function with given fields: _a0, _a1
|
||||
func (_m *MockRootCoord) CreatePartitionV2(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreatePartitionV2")
|
||||
}
|
||||
|
||||
var r0 *rootcoordpb.CreatePartitionResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error)); ok {
|
||||
return rf(_a0, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest) *rootcoordpb.CreatePartitionResponse); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*rootcoordpb.CreatePartitionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.CreatePartitionRequest) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockRootCoord_CreatePartitionV2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePartitionV2'
|
||||
type MockRootCoord_CreatePartitionV2_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreatePartitionV2 is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *milvuspb.CreatePartitionRequest
|
||||
func (_e *MockRootCoord_Expecter) CreatePartitionV2(_a0 interface{}, _a1 interface{}) *MockRootCoord_CreatePartitionV2_Call {
|
||||
return &MockRootCoord_CreatePartitionV2_Call{Call: _e.mock.On("CreatePartitionV2", _a0, _a1)}
|
||||
}
|
||||
|
||||
func (_c *MockRootCoord_CreatePartitionV2_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest)) *MockRootCoord_CreatePartitionV2_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(*milvuspb.CreatePartitionRequest))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRootCoord_CreatePartitionV2_Call) Return(_a0 *rootcoordpb.CreatePartitionResponse, _a1 error) *MockRootCoord_CreatePartitionV2_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRootCoord_CreatePartitionV2_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error)) *MockRootCoord_CreatePartitionV2_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePrivilegeGroup provides a mock function with given fields: _a0, _a1
|
||||
func (_m *MockRootCoord) CreatePrivilegeGroup(_a0 context.Context, _a1 *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
@@ -1558,6 +1558,80 @@ func (_c *MockRootCoordClient_CreatePartition_Call) RunAndReturn(run func(contex
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePartitionV2 provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *MockRootCoordClient) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
for _i := range opts {
|
||||
_va[_i] = opts[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, in)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CreatePartitionV2")
|
||||
}
|
||||
|
||||
var r0 *rootcoordpb.CreatePartitionResponse
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error)); ok {
|
||||
return rf(ctx, in, opts...)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) *rootcoordpb.CreatePartitionResponse); ok {
|
||||
r0 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*rootcoordpb.CreatePartitionResponse)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) error); ok {
|
||||
r1 = rf(ctx, in, opts...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockRootCoordClient_CreatePartitionV2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePartitionV2'
|
||||
type MockRootCoordClient_CreatePartitionV2_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// CreatePartitionV2 is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - in *milvuspb.CreatePartitionRequest
|
||||
// - opts ...grpc.CallOption
|
||||
func (_e *MockRootCoordClient_Expecter) CreatePartitionV2(ctx interface{}, in interface{}, opts ...interface{}) *MockRootCoordClient_CreatePartitionV2_Call {
|
||||
return &MockRootCoordClient_CreatePartitionV2_Call{Call: _e.mock.On("CreatePartitionV2",
|
||||
append([]interface{}{ctx, in}, opts...)...)}
|
||||
}
|
||||
|
||||
func (_c *MockRootCoordClient_CreatePartitionV2_Call) Run(run func(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption)) *MockRootCoordClient_CreatePartitionV2_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]grpc.CallOption, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(grpc.CallOption)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*milvuspb.CreatePartitionRequest), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRootCoordClient_CreatePartitionV2_Call) Return(_a0 *rootcoordpb.CreatePartitionResponse, _a1 error) *MockRootCoordClient_CreatePartitionV2_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockRootCoordClient_CreatePartitionV2_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePartitionRequest, ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error)) *MockRootCoordClient_CreatePartitionV2_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// CreatePrivilegeGroup provides a mock function with given fields: ctx, in, opts
|
||||
func (_m *MockRootCoordClient) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||
_va := make([]interface{}, len(opts))
|
||||
|
||||
@@ -385,8 +385,10 @@ func TestRemoveCollectionByID_CleansUpAliases(t *testing.T) {
|
||||
"alias3": {collectionName: "other_collection", cachedAt: time.Now()},
|
||||
},
|
||||
},
|
||||
collectionCacheVersion: make(map[UniqueID]uint64),
|
||||
sfGlobal: conc.Singleflight[*collectionInfo]{},
|
||||
collectionCacheVersion: make(map[UniqueID]uint64),
|
||||
sfGlobal: conc.Singleflight[*collectionInfo]{},
|
||||
collLevelPartitionCache: NewVersionCache[string, *partitionInfos](),
|
||||
partitionCache: NewVersionCache[string, *partitionInfo](),
|
||||
}
|
||||
|
||||
// Remove collection by ID
|
||||
@@ -457,6 +459,8 @@ func TestRemoveDatabase_CleansUpAliases(t *testing.T) {
|
||||
dbInfo: map[string]*databaseInfo{
|
||||
"mydb": {},
|
||||
},
|
||||
collLevelPartitionCache: NewVersionCache[string, *partitionInfos](),
|
||||
partitionCache: NewVersionCache[string, *partitionInfo](),
|
||||
}
|
||||
|
||||
cache.RemoveDatabase(ctx, "mydb")
|
||||
|
||||
@@ -183,11 +183,7 @@ func (node *Proxy) InvalidateCollectionMetaCache(ctx context.Context, request *p
|
||||
log.Warn("invalidate collection meta cache failed. partitionName is empty")
|
||||
return &commonpb.Status{ErrorCode: commonpb.ErrorCode_UnexpectedError}, nil
|
||||
}
|
||||
// drop all the alias as well
|
||||
if request.CollectionID != UniqueID(0) {
|
||||
aliasName = globalMetaCache.RemoveCollectionsByID(ctx, collectionID, request.GetBase().GetTimestamp(), false)
|
||||
}
|
||||
globalMetaCache.RemoveCollection(ctx, request.GetDbName(), collectionName, request.GetBase().GetTimestamp())
|
||||
globalMetaCache.RemovePartition(ctx, request.GetDbName(), collectionID, collectionName, request.GetPartitionName(), request.GetBase().GetTimestamp())
|
||||
log.Info("complete to invalidate collection meta cache", zap.String("type", request.GetBase().GetMsgType().String()))
|
||||
case commonpb.MsgType_DropDatabase:
|
||||
node.shardMgr.RemoveDatabase(request.GetDbName())
|
||||
|
||||
@@ -979,6 +979,10 @@ func TestProxyDropDatabase(t *testing.T) {
|
||||
t.Run("drop database ok", func(t *testing.T) {
|
||||
mix := mocks.NewMockMixCoordClient(t)
|
||||
mix.EXPECT().DropDatabase(mock.Anything, mock.Anything).Return(merr.Success(), nil)
|
||||
mix.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
mix.EXPECT().ShowLoadCollections(mock.Anything, mock.Anything).Return(&querypb.ShowCollectionsResponse{}, nil).Maybe()
|
||||
node.mixCoord = mix
|
||||
node.UpdateStateCode(commonpb.StateCode_Healthy)
|
||||
|
||||
|
||||
+322
-58
@@ -99,13 +99,15 @@ type Cache interface {
|
||||
GetDatabaseInfo(ctx context.Context, database string) (*databaseInfo, error)
|
||||
// AllocID is only using on requests that need to skip timestamp allocation, don't overuse it.
|
||||
AllocID(ctx context.Context) (int64, error)
|
||||
|
||||
RemovePartition(ctx context.Context, database string, collectionID UniqueID, collectionName string, partitionName string, version uint64)
|
||||
Close()
|
||||
}
|
||||
|
||||
type collectionInfo struct {
|
||||
collID typeutil.UniqueID
|
||||
dbName string
|
||||
schema *schemaInfo
|
||||
partInfo *partitionInfos
|
||||
createdTimestamp uint64
|
||||
createdUtcTimestamp uint64
|
||||
consistencyLevel commonpb.ConsistencyLevel
|
||||
@@ -374,6 +376,15 @@ type MetaCache struct {
|
||||
IDLock sync.RWMutex
|
||||
|
||||
collectionCacheVersion map[UniqueID]uint64 // collectionID -> cacheVersion
|
||||
|
||||
partitionCache *VersionCache[string, *partitionInfo] // partitionName -> partitionInfo
|
||||
collLevelPartitionCache *VersionCache[string, *partitionInfos] // collectionName -> partitionInfos
|
||||
|
||||
sfPartitionCache conc.Singleflight[*partitionInfo]
|
||||
sfCollLevelPartitionCache conc.Singleflight[*partitionInfos]
|
||||
|
||||
stopCh chan struct{}
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
// globalMetaCache is singleton instance of Cache
|
||||
@@ -402,16 +413,22 @@ func InitMetaCache(ctx context.Context, mixCoord types.MixCoordClient) error {
|
||||
|
||||
// NewMetaCache creates a MetaCache with provided RootCoord and QueryNode
|
||||
func NewMetaCache(mixCoord types.MixCoordClient) (*MetaCache, error) {
|
||||
return &MetaCache{
|
||||
mixCoord: mixCoord,
|
||||
dbInfo: map[string]*databaseInfo{},
|
||||
collInfo: map[string]map[string]*collectionInfo{},
|
||||
aliasInfo: map[string]map[string]*aliasEntry{},
|
||||
credMap: map[string]*internalpb.CredentialInfo{},
|
||||
privilegeInfos: map[string]struct{}{},
|
||||
userToRoles: map[string]map[string]struct{}{},
|
||||
collectionCacheVersion: make(map[UniqueID]uint64),
|
||||
}, nil
|
||||
metaCache := &MetaCache{
|
||||
mixCoord: mixCoord,
|
||||
dbInfo: map[string]*databaseInfo{},
|
||||
aliasInfo: map[string]map[string]*aliasEntry{},
|
||||
collInfo: map[string]map[string]*collectionInfo{},
|
||||
credMap: map[string]*internalpb.CredentialInfo{},
|
||||
privilegeInfos: map[string]struct{}{},
|
||||
userToRoles: map[string]map[string]struct{}{},
|
||||
collectionCacheVersion: make(map[UniqueID]uint64),
|
||||
partitionCache: NewVersionCache[string, *partitionInfo](),
|
||||
collLevelPartitionCache: NewVersionCache[string, *partitionInfos](),
|
||||
stopCh: make(chan struct{}),
|
||||
closeOnce: sync.Once{},
|
||||
}
|
||||
metaCache.backgroundGCLoop(metaCache.stopCh)
|
||||
return metaCache, nil
|
||||
}
|
||||
|
||||
func (m *MetaCache) getCollection(database, collectionName string, collectionID UniqueID) (*collectionInfo, bool) {
|
||||
@@ -455,27 +472,6 @@ func (m *MetaCache) update(ctx context.Context, database, collectionName string,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
partitions, err := m.showPartitions(ctx, database, collectionName, collectionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check partitionID, createdTimestamp and utcstamp has sam element numbers
|
||||
if len(partitions.PartitionNames) != len(partitions.CreatedTimestamps) || len(partitions.PartitionNames) != len(partitions.CreatedUtcTimestamps) {
|
||||
return nil, merr.WrapErrParameterInvalidMsg("partition names and timestamps number is not aligned, response: %s", partitions.String())
|
||||
}
|
||||
|
||||
defaultPartitionName := Params.CommonCfg.DefaultPartitionName.GetValue()
|
||||
infos := lo.Map(partitions.GetPartitionIDs(), func(partitionID int64, idx int) *partitionInfo {
|
||||
return &partitionInfo{
|
||||
name: partitions.PartitionNames[idx],
|
||||
partitionID: partitions.PartitionIDs[idx],
|
||||
createdTimestamp: partitions.CreatedTimestamps[idx],
|
||||
createdUtcTimestamp: partitions.CreatedUtcTimestamps[idx],
|
||||
isDefault: partitions.PartitionNames[idx] == defaultPartitionName,
|
||||
}
|
||||
})
|
||||
|
||||
realName := collection.Schema.GetName()
|
||||
originalName := collectionName
|
||||
isAlias := collectionName != "" && realName != "" && realName != collectionName
|
||||
@@ -505,7 +501,6 @@ func (m *MetaCache) update(ctx context.Context, database, collectionName string,
|
||||
collID: collection.CollectionID,
|
||||
dbName: collection.GetDbName(),
|
||||
schema: schemaInfo,
|
||||
partInfo: parsePartitionsInfo(infos, schemaInfo.hasPartitionKeyField),
|
||||
createdTimestamp: collection.CreatedTimestamp,
|
||||
createdUtcTimestamp: collection.CreatedUtcTimestamp,
|
||||
consistencyLevel: collection.ConsistencyLevel,
|
||||
@@ -538,7 +533,6 @@ func (m *MetaCache) update(ctx context.Context, database, collectionName string,
|
||||
collID: collection.CollectionID,
|
||||
dbName: collection.GetDbName(),
|
||||
schema: schemaInfo,
|
||||
partInfo: parsePartitionsInfo(infos, schemaInfo.hasPartitionKeyField),
|
||||
createdTimestamp: collection.CreatedTimestamp,
|
||||
createdUtcTimestamp: collection.CreatedUtcTimestamp,
|
||||
consistencyLevel: collection.ConsistencyLevel,
|
||||
@@ -556,7 +550,6 @@ func (m *MetaCache) update(ctx context.Context, database, collectionName string,
|
||||
|
||||
log.Ctx(ctx).Info("meta update success", zap.String("database", database), zap.String("collectionName", collectionName),
|
||||
zap.String("actual collection Name", collection.Schema.GetName()), zap.Int64("collectionID", collection.CollectionID),
|
||||
zap.Strings("partition", partitions.PartitionNames), zap.Uint64("currentVersion", curVersion),
|
||||
zap.Uint64("version", collection.GetRequestTime()), zap.Any("aliases", collection.Aliases),
|
||||
zap.Bool("partition key isolation", isolation), zap.String("queryMode", queryMode),
|
||||
)
|
||||
@@ -575,6 +568,10 @@ func buildSfKeyById(database string, collectionID UniqueID) string {
|
||||
return database + "--" + fmt.Sprint(collectionID)
|
||||
}
|
||||
|
||||
func buildPartitionSfKey(database, collectionName, partitionName string) string {
|
||||
return database + "-" + collectionName + "-" + partitionName
|
||||
}
|
||||
|
||||
func (m *MetaCache) UpdateByName(ctx context.Context, database, collectionName string) (*collectionInfo, error) {
|
||||
collection, err, _ := m.sfGlobal.Do(buildSfKeyByName(database, collectionName), func() (*collectionInfo, error) {
|
||||
return m.update(ctx, database, collectionName, 0)
|
||||
@@ -824,24 +821,55 @@ func (m *MetaCache) GetPartitions(ctx context.Context, database, collectionName
|
||||
}
|
||||
|
||||
func (m *MetaCache) GetPartitionInfo(ctx context.Context, database, collectionName string, partitionName string) (*partitionInfo, error) {
|
||||
partitions, err := m.GetPartitionInfos(ctx, database, collectionName)
|
||||
// Handle empty partitionName - use default partition
|
||||
if partitionName == "" {
|
||||
partitionName = Params.CommonCfg.DefaultPartitionName.GetValue()
|
||||
}
|
||||
|
||||
key := buildPartitionSfKey(database, collectionName, partitionName)
|
||||
entry, ok, release := m.partitionCache.Lookup(key)
|
||||
defer release(entry)
|
||||
if ok && entry.state == EntryStateActive && entry.value != nil {
|
||||
return entry.value, nil
|
||||
}
|
||||
|
||||
collectionKey := buildSfKeyByName(database, collectionName)
|
||||
_, err, _ := m.sfPartitionCache.Do(collectionKey, func() (*partitionInfo, error) {
|
||||
// as rootcoord does not support show partitions by partition name, we need to get all partitions first.
|
||||
resp, err := m.showPartitions(ctx, database, collectionName, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys := make([]string, 0)
|
||||
values := make([]*partitionInfo, 0)
|
||||
versions := make([]uint64, 0)
|
||||
var ret *partitionInfo
|
||||
for i := range resp.PartitionNames {
|
||||
keys = append(keys, buildPartitionSfKey(database, collectionName, resp.PartitionNames[i]))
|
||||
values = append(values, &partitionInfo{
|
||||
name: resp.PartitionNames[i],
|
||||
partitionID: resp.PartitionIDs[i],
|
||||
createdTimestamp: resp.CreatedTimestamps[i],
|
||||
createdUtcTimestamp: resp.CreatedUtcTimestamps[i],
|
||||
})
|
||||
versions = append(versions, resp.CreatedTimestamps[i])
|
||||
if resp.PartitionNames[i] == partitionName {
|
||||
ret = values[i]
|
||||
}
|
||||
}
|
||||
m.partitionCache.InsertBatchWithoutRef(keys, values, versions)
|
||||
return ret, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if partitionName == "" {
|
||||
for _, info := range partitions.partitionInfos {
|
||||
if info.isDefault {
|
||||
return info, nil
|
||||
}
|
||||
}
|
||||
entry, ok, release = m.partitionCache.Lookup(key)
|
||||
defer release(entry)
|
||||
if ok && entry.state == EntryStateActive && entry.value != nil {
|
||||
return entry.value, nil
|
||||
}
|
||||
|
||||
info, ok := partitions.name2Info[partitionName]
|
||||
if !ok {
|
||||
return nil, merr.WrapErrPartitionNotFound(partitionName)
|
||||
}
|
||||
return info, nil
|
||||
return nil, merr.WrapErrPartitionNotFound(partitionName)
|
||||
}
|
||||
|
||||
func (m *MetaCache) GetPartitionsIndex(ctx context.Context, database, collectionName string) ([]string, error) {
|
||||
@@ -859,21 +887,47 @@ func (m *MetaCache) GetPartitionsIndex(ctx context.Context, database, collection
|
||||
|
||||
func (m *MetaCache) GetPartitionInfos(ctx context.Context, database, collectionName string) (*partitionInfos, error) {
|
||||
method := "GetPartitionInfo"
|
||||
collInfo, ok := m.getCollection(database, collectionName, 0)
|
||||
|
||||
if !ok {
|
||||
tr := timerecord.NewTimeRecorder("UpdateCache")
|
||||
metrics.ProxyCacheStatsCounter.WithLabelValues(paramtable.GetStringNodeID(), method, metrics.CacheMissLabel).Inc()
|
||||
|
||||
collInfo, err := m.UpdateByName(ctx, database, collectionName)
|
||||
key := buildSfKeyByName(database, collectionName)
|
||||
entry, ok, release := m.collLevelPartitionCache.Lookup(key)
|
||||
defer release(entry)
|
||||
if ok && entry.state == EntryStateActive && entry.value != nil {
|
||||
return entry.value, nil
|
||||
}
|
||||
tr := timerecord.NewTimeRecorder("UpdateCache")
|
||||
metrics.ProxyCacheStatsCounter.WithLabelValues(paramtable.GetStringNodeID(), method, metrics.CacheMissLabel).Inc()
|
||||
partitionsInfo, err, _ := m.sfCollLevelPartitionCache.Do(key, func() (*partitionInfos, error) {
|
||||
collection, err := m.describeCollection(ctx, database, collectionName, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaInfo := newSchemaInfo(collection.Schema)
|
||||
|
||||
resp, err := m.showPartitions(ctx, database, collectionName, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
partitions := make([]*partitionInfo, 0)
|
||||
for i, name := range resp.PartitionNames {
|
||||
partitions = append(partitions, &partitionInfo{
|
||||
name: name,
|
||||
partitionID: resp.PartitionIDs[i],
|
||||
createdTimestamp: resp.CreatedTimestamps[i],
|
||||
createdUtcTimestamp: resp.CreatedUtcTimestamps[i],
|
||||
})
|
||||
}
|
||||
partitionsInfo := parsePartitionsInfo(partitions, schemaInfo.IsPartitionKeyCollection())
|
||||
entry, release := m.collLevelPartitionCache.Insert(key, partitionsInfo, collection.RequestTime)
|
||||
defer release(entry)
|
||||
metrics.ProxyUpdateCacheLatency.WithLabelValues(paramtable.GetStringNodeID(), method).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
||||
return collInfo.partInfo, nil
|
||||
return entry.value, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return collInfo.partInfo, nil
|
||||
if partitionsInfo == nil {
|
||||
return nil, merr.WrapErrServiceInternal("partition info not found")
|
||||
}
|
||||
return partitionsInfo, nil
|
||||
}
|
||||
|
||||
// Get the collection information from rootcoord.
|
||||
@@ -927,6 +981,13 @@ func (m *MetaCache) showPartitions(ctx context.Context, dbName string, collectio
|
||||
return nil, fmt.Errorf("partition ids len: %d doesn't equal Partition name len %d",
|
||||
len(partitions.PartitionIDs), len(partitions.PartitionNames))
|
||||
}
|
||||
if len(partitions.PartitionNames) != len(partitions.CreatedTimestamps) ||
|
||||
len(partitions.PartitionNames) != len(partitions.CreatedUtcTimestamps) {
|
||||
return nil, merr.WrapErrParameterInvalidMsg(
|
||||
"partition names and timestamps number is not aligned, response: %s",
|
||||
partitions.String(),
|
||||
)
|
||||
}
|
||||
|
||||
return partitions, nil
|
||||
}
|
||||
@@ -1032,13 +1093,22 @@ func (m *MetaCache) removeCollectionByID(ctx context.Context, collectionID Uniqu
|
||||
if version == 0 || curVersion <= version {
|
||||
delete(m.collInfo[database], k)
|
||||
collNames = append(collNames, k)
|
||||
m.sfGlobal.Forget(buildSfKeyByName(database, k))
|
||||
collectionKey := buildSfKeyByName(database, k)
|
||||
m.sfGlobal.Forget(collectionKey)
|
||||
m.sfGlobal.Forget(buildSfKeyById(database, v.collID))
|
||||
realName := k
|
||||
if v.schema != nil && v.schema.GetName() != "" {
|
||||
realName = v.schema.GetName()
|
||||
}
|
||||
m.removeAliasesForCollectionLocked(database, realName)
|
||||
m.sfCollLevelPartitionCache.Forget(collectionKey)
|
||||
m.collLevelPartitionCache.Stale(collectionKey, version)
|
||||
|
||||
partitionPrefix := database + "-" + realName + "-"
|
||||
m.sfPartitionCache.Forget(collectionKey)
|
||||
m.partitionCache.StaleIf(func(key string) bool {
|
||||
return strings.HasPrefix(key, partitionPrefix)
|
||||
}, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1058,9 +1128,28 @@ func (m *MetaCache) RemoveDatabase(ctx context.Context, database string) {
|
||||
log.Ctx(ctx).Debug("remove database", zap.String("name", database))
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// Forget singleflight keys for all collections in this database
|
||||
if db, ok := m.collInfo[database]; ok {
|
||||
for collectionName := range db {
|
||||
collectionKey := buildSfKeyByName(database, collectionName)
|
||||
m.sfCollLevelPartitionCache.Forget(collectionKey)
|
||||
m.sfPartitionCache.Forget(collectionKey)
|
||||
}
|
||||
}
|
||||
|
||||
delete(m.collInfo, database)
|
||||
delete(m.dbInfo, database)
|
||||
delete(m.aliasInfo, database)
|
||||
|
||||
// Clean up partition cache
|
||||
prefix := database + "-"
|
||||
m.collLevelPartitionCache.StaleIf(func(key string) bool {
|
||||
return strings.HasPrefix(key, prefix)
|
||||
}, 0)
|
||||
m.partitionCache.StaleIf(func(key string) bool {
|
||||
return strings.HasPrefix(key, prefix)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
func (m *MetaCache) HasDatabase(ctx context.Context, database string) bool {
|
||||
@@ -1129,3 +1218,178 @@ func (m *MetaCache) AllocID(ctx context.Context) (int64, error) {
|
||||
m.IDIndex++
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (m *MetaCache) RemovePartition(ctx context.Context, database string, collectionID UniqueID, collectionName string, partitionName string, version uint64) {
|
||||
m.ensureCollectionForPartitionInvalidation(ctx, database, collectionID, collectionName)
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
names := make(map[string]struct{})
|
||||
realNames := make(map[string]struct{})
|
||||
for _, dbName := range partitionInvalidationDatabases(database) {
|
||||
m.collectPartitionCacheNamesLocked(dbName, collectionID, collectionName, names, realNames)
|
||||
for name := range names {
|
||||
if coll, ok := m.getCollectionLocked(dbName, name); ok && coll.schema != nil {
|
||||
realName := coll.schema.GetName()
|
||||
if realName != "" {
|
||||
realNames[realName] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
if aliases, ok := m.aliasInfo[dbName]; ok {
|
||||
for alias, entry := range aliases {
|
||||
if entry == nil || entry.collectionName == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := realNames[entry.collectionName]; ok {
|
||||
names[alias] = struct{}{}
|
||||
names[entry.collectionName] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for name := range names {
|
||||
m.stalePartitionCacheLocked(dbName, name, partitionName, version)
|
||||
}
|
||||
clear(names)
|
||||
clear(realNames)
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Debug("remove partition", zap.String("db", database), zap.Int64("collectionID", collectionID), zap.String("collection", collectionName), zap.String("partition", partitionName), zap.Uint64("version", version))
|
||||
}
|
||||
|
||||
func (m *MetaCache) ensureCollectionForPartitionInvalidation(ctx context.Context, database string, collectionID UniqueID, collectionName string) {
|
||||
if collectionID != 0 {
|
||||
for _, dbName := range partitionInvalidationDatabases(database) {
|
||||
if _, ok := m.getCollection(dbName, "", collectionID); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fetchDB := database
|
||||
if fetchDB == "" {
|
||||
fetchDB = defaultDB
|
||||
}
|
||||
if _, err := m.UpdateByID(ctx, fetchDB, collectionID); err != nil {
|
||||
log.Ctx(ctx).Debug("failed to refresh collection cache before partition invalidation",
|
||||
zap.String("db", fetchDB),
|
||||
zap.Int64("collectionID", collectionID),
|
||||
zap.Error(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if collectionName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dbName := range partitionInvalidationDatabases(database) {
|
||||
if _, ok := m.getCollection(dbName, collectionName, 0); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fetchDB := database
|
||||
if fetchDB == "" {
|
||||
fetchDB = defaultDB
|
||||
}
|
||||
if _, err := m.UpdateByName(ctx, fetchDB, collectionName); err != nil {
|
||||
log.Ctx(ctx).Debug("failed to refresh collection cache by name before partition invalidation",
|
||||
zap.String("db", fetchDB),
|
||||
zap.String("collection", collectionName),
|
||||
zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
func partitionInvalidationDatabases(database string) []string {
|
||||
if database == "" {
|
||||
return []string{database, defaultDB}
|
||||
}
|
||||
return []string{database}
|
||||
}
|
||||
|
||||
func (m *MetaCache) getCollectionLocked(database, collectionName string) (*collectionInfo, bool) {
|
||||
db, ok := m.collInfo[database]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
collection, ok := db[collectionName]
|
||||
return collection, ok
|
||||
}
|
||||
|
||||
func (m *MetaCache) collectPartitionCacheNamesLocked(database string, collectionID UniqueID, collectionName string, names, realNames map[string]struct{}) {
|
||||
if collectionName != "" {
|
||||
names[collectionName] = struct{}{}
|
||||
if coll, ok := m.getCollectionLocked(database, collectionName); ok && coll.schema != nil {
|
||||
realName := coll.schema.GetName()
|
||||
if realName != "" {
|
||||
names[realName] = struct{}{}
|
||||
realNames[realName] = struct{}{}
|
||||
}
|
||||
for _, alias := range coll.aliases {
|
||||
names[alias] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if collectionID == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if db, ok := m.collInfo[database]; ok {
|
||||
for name, coll := range db {
|
||||
if coll.collID != collectionID {
|
||||
continue
|
||||
}
|
||||
names[name] = struct{}{}
|
||||
if coll.schema != nil {
|
||||
realName := coll.schema.GetName()
|
||||
if realName != "" {
|
||||
names[realName] = struct{}{}
|
||||
realNames[realName] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, alias := range coll.aliases {
|
||||
names[alias] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MetaCache) stalePartitionCacheLocked(database, collectionName, partitionName string, version uint64) {
|
||||
collectionKey := buildSfKeyByName(database, collectionName)
|
||||
m.sfPartitionCache.Forget(collectionKey)
|
||||
m.partitionCache.Stale(buildPartitionSfKey(database, collectionName, partitionName), version)
|
||||
m.sfCollLevelPartitionCache.Forget(collectionKey)
|
||||
m.collLevelPartitionCache.Stale(collectionKey, version)
|
||||
}
|
||||
|
||||
func (m *MetaCache) backgroundGCLoop(stopCh <-chan struct{}) {
|
||||
go func() {
|
||||
interval := Params.ProxyCfg.MetaCacheGCTimeInterval.GetAsDuration(time.Second)
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
m.partitionCache.Prune()
|
||||
m.collLevelPartitionCache.Prune()
|
||||
|
||||
newInterval := Params.ProxyCfg.MetaCacheGCTimeInterval.GetAsDuration(time.Second)
|
||||
if newInterval != interval {
|
||||
interval = newInterval
|
||||
ticker.Reset(interval)
|
||||
}
|
||||
case <-stopCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *MetaCache) Close() {
|
||||
m.closeOnce.Do(func() {
|
||||
close(m.stopCh)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1106,13 +1106,6 @@ func TestMetaCacheGetCollectionWithUpdate(t *testing.T) {
|
||||
PhysicalChannelNames: []string{"by-dev-rootcoord-dml_1"},
|
||||
VirtualChannelNames: []string{"by-dev-rootcoord-dml_1_1v0"},
|
||||
}, nil).Once()
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything, mock.Anything).Return(&milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []typeutil.UniqueID{11},
|
||||
PartitionNames: []string{"p1"},
|
||||
CreatedTimestamps: []uint64{11},
|
||||
CreatedUtcTimestamps: []uint64{11},
|
||||
}, nil).Once()
|
||||
c, err := globalMetaCache.GetCollectionInfo(ctx, "foo", "bar", 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.collID, int64(1))
|
||||
@@ -1140,13 +1133,6 @@ func TestMetaCacheGetCollectionWithUpdate(t *testing.T) {
|
||||
PhysicalChannelNames: []string{"by-dev-rootcoord-dml_1"},
|
||||
VirtualChannelNames: []string{"by-dev-rootcoord-dml_1_1v0"},
|
||||
}, nil).Once()
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything, mock.Anything).Return(&milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []typeutil.UniqueID{11},
|
||||
PartitionNames: []string{"p1"},
|
||||
CreatedTimestamps: []uint64{11},
|
||||
CreatedUtcTimestamps: []uint64{11},
|
||||
}, nil).Once()
|
||||
c, err := globalMetaCache.GetCollectionInfo(ctx, "foo", "hoo", 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.collID, int64(1))
|
||||
@@ -2072,3 +2058,426 @@ func TestMetaCache_GetShardLeaderList(t *testing.T) {
|
||||
t.Skip("GetShardLeaderList has been moved to ShardClientMgr in shardclient package")
|
||||
// Test body removed - functionality moved to shardclient package
|
||||
}
|
||||
|
||||
func TestVersionCache(t *testing.T) {
|
||||
t.Run("Lookup_Miss", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
entry, ok, release := cache.Lookup("key1")
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, entry)
|
||||
release(entry)
|
||||
})
|
||||
|
||||
t.Run("Insert_And_Lookup", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
value := 100
|
||||
entry, release := cache.Insert("key1", &value, 1000)
|
||||
assert.NotNil(t, entry)
|
||||
assert.Equal(t, 100, *entry.value)
|
||||
release(entry)
|
||||
|
||||
entry2, ok, release2 := cache.Lookup("key1")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 100, *entry2.value)
|
||||
release2(entry2)
|
||||
})
|
||||
|
||||
t.Run("Insert_Higher_Version_Overwrites", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
value1 := 100
|
||||
entry1, release1 := cache.Insert("key1", &value1, 1000)
|
||||
release1(entry1)
|
||||
|
||||
value2 := 200
|
||||
entry2, release2 := cache.Insert("key1", &value2, 2000)
|
||||
assert.Equal(t, 200, *entry2.value)
|
||||
release2(entry2)
|
||||
|
||||
entry3, ok, release3 := cache.Lookup("key1")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, 200, *entry3.value)
|
||||
release3(entry3)
|
||||
})
|
||||
|
||||
t.Run("Insert_Lower_Version_Ignored", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
value1 := 100
|
||||
entry1, release1 := cache.Insert("key1", &value1, 2000)
|
||||
release1(entry1)
|
||||
|
||||
value2 := 200
|
||||
entry2, release2 := cache.Insert("key1", &value2, 1000)
|
||||
assert.Equal(t, 100, *entry2.value)
|
||||
release2(entry2)
|
||||
})
|
||||
|
||||
t.Run("Stale_Erase_When_No_Refs", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
value := 100
|
||||
entry, release := cache.Insert("key1", &value, 1000)
|
||||
release(entry)
|
||||
|
||||
cache.Stale("key1", 2000)
|
||||
|
||||
_, ok, release2 := cache.Lookup("key1")
|
||||
assert.False(t, ok)
|
||||
release2(nil)
|
||||
})
|
||||
|
||||
t.Run("Stale_Marks_Entry_With_Active_Refs", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
value := 100
|
||||
entry, release := cache.Insert("key1", &value, 1000)
|
||||
assert.Equal(t, EntryStateActive, entry.state)
|
||||
|
||||
cache.Stale("key1", 2000)
|
||||
|
||||
// Entry should be marked as stale
|
||||
entry2, ok, release2 := cache.Lookup("key1")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, EntryStateStale, entry2.state)
|
||||
release2(entry2)
|
||||
|
||||
release(entry)
|
||||
|
||||
// After release, Stale should erase
|
||||
cache.Stale("key1", 3000)
|
||||
_, ok, release3 := cache.Lookup("key1")
|
||||
assert.False(t, ok)
|
||||
release3(nil)
|
||||
})
|
||||
|
||||
t.Run("Prune_Only_Stale_Entries", func(t *testing.T) {
|
||||
cache := NewVersionCache[string, *int]()
|
||||
|
||||
// key1: Active, ref=0 - should NOT be pruned
|
||||
value1 := 100
|
||||
entry1, release1 := cache.Insert("key1", &value1, 1000)
|
||||
release1(entry1)
|
||||
|
||||
// key2: Stale, ref=0 - should be pruned
|
||||
value2 := 200
|
||||
entry2, release2 := cache.Insert("key2", &value2, 2000)
|
||||
release2(entry2)
|
||||
cache.Stale("key2", 2500)
|
||||
|
||||
// key3: Stale, ref=1 - should NOT be pruned
|
||||
value3 := 300
|
||||
entry3, _ := cache.Insert("key3", &value3, 3000)
|
||||
cache.Stale("key3", 3500)
|
||||
|
||||
cache.Prune()
|
||||
|
||||
// key1 should still exist (Active, ref=0)
|
||||
entry1Found, ok1, release1Found := cache.Lookup("key1")
|
||||
assert.True(t, ok1)
|
||||
assert.Equal(t, 100, *entry1Found.value)
|
||||
release1Found(entry1Found)
|
||||
|
||||
// key2 should be gone (Stale, ref=0)
|
||||
_, ok2, release2Found := cache.Lookup("key2")
|
||||
assert.False(t, ok2)
|
||||
release2Found(nil)
|
||||
|
||||
// key3 should still exist (Stale, ref=1)
|
||||
entry3Found, ok3, release3Found := cache.Lookup("key3")
|
||||
assert.True(t, ok3)
|
||||
assert.Equal(t, EntryStateStale, entry3Found.state)
|
||||
release3Found(entry3Found)
|
||||
|
||||
// Release the ref on entry3
|
||||
release1(entry3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMetaCache_GetPartitionInfo_CacheHit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything).Return(&milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []int64{100},
|
||||
PartitionNames: []string{"par1"},
|
||||
CreatedTimestamps: []uint64{1000},
|
||||
CreatedUtcTimestamps: []uint64{1000},
|
||||
}, nil).Once()
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
info1, err := cache.GetPartitionInfo(ctx, "db", "collection", "par1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(100), info1.partitionID)
|
||||
assert.Equal(t, "par1", info1.name)
|
||||
|
||||
info2, err := cache.GetPartitionInfo(ctx, "db", "collection", "par1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, info1, info2)
|
||||
}
|
||||
|
||||
func TestMetaCache_GetPartitionInfo_DefaultPartition(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
defaultPartitionName := Params.CommonCfg.DefaultPartitionName.GetValue()
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.MatchedBy(func(req *milvuspb.ShowPartitionsRequest) bool {
|
||||
return len(req.PartitionNames) == 0
|
||||
})).Return(&milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []int64{1},
|
||||
PartitionNames: []string{defaultPartitionName},
|
||||
CreatedTimestamps: []uint64{1000},
|
||||
CreatedUtcTimestamps: []uint64{1000},
|
||||
}, nil).Once()
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
info, err := cache.GetPartitionInfo(ctx, "db", "collection", "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), info.partitionID)
|
||||
assert.Equal(t, defaultPartitionName, info.name)
|
||||
}
|
||||
|
||||
func TestMetaCache_GetPartitionInfo_Error(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything).Return(nil, errors.New("connection failed")).Once()
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
_, err = cache.GetPartitionInfo(ctx, "db", "collection", "par1")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMetaCache_GetPartitionInfos_CacheHit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
rootCoord.EXPECT().DescribeCollection(mock.Anything, mock.Anything).Return(&milvuspb.DescribeCollectionResponse{
|
||||
Status: merr.Success(),
|
||||
CollectionID: 1,
|
||||
Schema: &schemapb.CollectionSchema{
|
||||
Name: "collection",
|
||||
Fields: []*schemapb.FieldSchema{},
|
||||
},
|
||||
RequestTime: 1000,
|
||||
}, nil).Once()
|
||||
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything).Return(&milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []int64{100, 101},
|
||||
PartitionNames: []string{"par1", "par2"},
|
||||
CreatedTimestamps: []uint64{1000, 1001},
|
||||
CreatedUtcTimestamps: []uint64{1000, 1001},
|
||||
}, nil).Once()
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
infos1, err := cache.GetPartitionInfos(ctx, "db", "collection")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(infos1.partitionInfos))
|
||||
|
||||
infos2, err := cache.GetPartitionInfos(ctx, "db", "collection")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, infos1, infos2)
|
||||
}
|
||||
|
||||
func TestMetaCache_RemovePartition(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
rootCoord.EXPECT().DescribeCollection(mock.Anything, mock.Anything).Return(&milvuspb.DescribeCollectionResponse{
|
||||
Status: merr.Success(),
|
||||
CollectionID: 1,
|
||||
Schema: &schemapb.CollectionSchema{
|
||||
Name: "collection",
|
||||
Fields: []*schemapb.FieldSchema{},
|
||||
},
|
||||
Aliases: []string{"alias"},
|
||||
RequestTime: 1000,
|
||||
}, nil).Once()
|
||||
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything).Return(&milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []int64{100},
|
||||
PartitionNames: []string{"par1"},
|
||||
CreatedTimestamps: []uint64{1000},
|
||||
CreatedUtcTimestamps: []uint64{1000},
|
||||
}, nil).Times(4)
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
_, err = cache.GetPartitionInfo(ctx, "db", "collection", "par1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = cache.GetPartitionInfo(ctx, "db", "alias", "par1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
cache.RemovePartition(ctx, "db", 1, "alias", "par1", 2000)
|
||||
|
||||
_, err = cache.GetPartitionInfo(ctx, "db", "collection", "par1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = cache.GetPartitionInfo(ctx, "db", "alias", "par1")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestMetaCache_PartitionCache_Concurrent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
var callCount atomic.Int32
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything).RunAndReturn(
|
||||
func(ctx context.Context, req *milvuspb.ShowPartitionsRequest, opts ...grpc.CallOption) (*milvuspb.ShowPartitionsResponse, error) {
|
||||
callCount.Add(1)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
return &milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []int64{100},
|
||||
PartitionNames: []string{"par1"},
|
||||
CreatedTimestamps: []uint64{1000},
|
||||
CreatedUtcTimestamps: []uint64{1000},
|
||||
}, nil
|
||||
})
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
numGoroutines := 10
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, err := cache.GetPartitionInfo(ctx, "db", "collection", "par1")
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
assert.Equal(t, int32(1), callCount.Load(), "Singleflight should merge concurrent requests")
|
||||
}
|
||||
|
||||
func TestMetaCache_GetPartitionInfos_SingleflightKeyIncludesDatabase(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
rootCoord.EXPECT().DescribeCollection(mock.Anything, mock.Anything).RunAndReturn(
|
||||
func(ctx context.Context, req *milvuspb.DescribeCollectionRequest, opts ...grpc.CallOption) (*milvuspb.DescribeCollectionResponse, error) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
return &milvuspb.DescribeCollectionResponse{
|
||||
Status: merr.Success(),
|
||||
CollectionID: 1,
|
||||
Schema: &schemapb.CollectionSchema{
|
||||
Name: req.GetCollectionName(),
|
||||
Fields: []*schemapb.FieldSchema{},
|
||||
},
|
||||
RequestTime: 1000,
|
||||
}, nil
|
||||
}).Twice()
|
||||
|
||||
rootCoord.EXPECT().ShowPartitions(mock.Anything, mock.Anything).RunAndReturn(
|
||||
func(ctx context.Context, req *milvuspb.ShowPartitionsRequest, opts ...grpc.CallOption) (*milvuspb.ShowPartitionsResponse, error) {
|
||||
return &milvuspb.ShowPartitionsResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionIDs: []int64{100},
|
||||
PartitionNames: []string{req.GetDbName() + "_par"},
|
||||
CreatedTimestamps: []uint64{1000},
|
||||
CreatedUtcTimestamps: []uint64{1000},
|
||||
}, nil
|
||||
}).Twice()
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
defer cache.Close()
|
||||
|
||||
type result struct {
|
||||
db string
|
||||
infos *partitionInfos
|
||||
err error
|
||||
}
|
||||
|
||||
results := make(chan result, 2)
|
||||
var wg sync.WaitGroup
|
||||
start := make(chan struct{})
|
||||
|
||||
for _, db := range []string{"db1", "db2"} {
|
||||
wg.Add(1)
|
||||
go func(db string) {
|
||||
defer wg.Done()
|
||||
<-start
|
||||
infos, err := cache.GetPartitionInfos(ctx, db, "collection")
|
||||
results <- result{
|
||||
db: db,
|
||||
infos: infos,
|
||||
err: err,
|
||||
}
|
||||
}(db)
|
||||
}
|
||||
|
||||
close(start)
|
||||
wg.Wait()
|
||||
close(results)
|
||||
|
||||
for result := range results {
|
||||
assert.NoError(t, result.err)
|
||||
if assert.NotNil(t, result.infos) && assert.Len(t, result.infos.partitionInfos, 1) {
|
||||
assert.Equal(t, result.db+"_par", result.infos.partitionInfos[0].name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetaCache_Close(t *testing.T) {
|
||||
rootCoord := mocks.NewMockMixCoordClient(t)
|
||||
|
||||
rootCoord.EXPECT().ListPolicy(mock.Anything, mock.Anything).Return(&internalpb.ListPolicyResponse{
|
||||
Status: merr.Success(),
|
||||
}, nil).Maybe()
|
||||
|
||||
cache, err := NewMetaCache(rootCoord)
|
||||
assert.NoError(t, err)
|
||||
|
||||
cache.Close()
|
||||
cache.Close()
|
||||
}
|
||||
|
||||
@@ -77,6 +77,38 @@ func (_c *MockCache_AllocID_Call) RunAndReturn(run func(context.Context) (int64,
|
||||
return _c
|
||||
}
|
||||
|
||||
// Close provides a mock function with no fields
|
||||
func (_m *MockCache) Close() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// MockCache_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close'
|
||||
type MockCache_Close_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// Close is a helper method to define mock.On call
|
||||
func (_e *MockCache_Expecter) Close() *MockCache_Close_Call {
|
||||
return &MockCache_Close_Call{Call: _e.mock.On("Close")}
|
||||
}
|
||||
|
||||
func (_c *MockCache_Close_Call) Run(run func()) *MockCache_Close_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run()
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockCache_Close_Call) Return() *MockCache_Close_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockCache_Close_Call) RunAndReturn(run func()) *MockCache_Close_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetCollectionID provides a mock function with given fields: ctx, database, collectionName
|
||||
func (_m *MockCache) GetCollectionID(ctx context.Context, database string, collectionName string) (int64, error) {
|
||||
ret := _m.Called(ctx, database, collectionName)
|
||||
@@ -875,6 +907,44 @@ func (_c *MockCache_RemoveDatabase_Call) RunAndReturn(run func(context.Context,
|
||||
return _c
|
||||
}
|
||||
|
||||
// RemovePartition provides a mock function with given fields: ctx, database, collectionID, collectionName, partitionName, version
|
||||
func (_m *MockCache) RemovePartition(ctx context.Context, database string, collectionID UniqueID, collectionName string, partitionName string, version uint64) {
|
||||
_m.Called(ctx, database, collectionID, collectionName, partitionName, version)
|
||||
}
|
||||
|
||||
// MockCache_RemovePartition_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RemovePartition'
|
||||
type MockCache_RemovePartition_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// RemovePartition is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
// - database string
|
||||
// - collectionID UniqueID
|
||||
// - collectionName string
|
||||
// - partitionName string
|
||||
// - version uint64
|
||||
func (_e *MockCache_Expecter) RemovePartition(ctx interface{}, database interface{}, collectionID interface{}, collectionName interface{}, partitionName interface{}, version interface{}) *MockCache_RemovePartition_Call {
|
||||
return &MockCache_RemovePartition_Call{Call: _e.mock.On("RemovePartition", ctx, database, collectionID, collectionName, partitionName, version)}
|
||||
}
|
||||
|
||||
func (_c *MockCache_RemovePartition_Call) Run(run func(ctx context.Context, database string, collectionID UniqueID, collectionName string, partitionName string, version uint64)) *MockCache_RemovePartition_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string), args[2].(UniqueID), args[3].(string), args[4].(string), args[5].(uint64))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockCache_RemovePartition_Call) Return() *MockCache_RemovePartition_Call {
|
||||
_c.Call.Return()
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockCache_RemovePartition_Call) RunAndReturn(run func(context.Context, string, UniqueID, string, string, uint64)) *MockCache_RemovePartition_Call {
|
||||
_c.Run(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ResolveCollectionAlias provides a mock function with given fields: ctx, database, nameOrAlias
|
||||
func (_m *MockCache) ResolveCollectionAlias(ctx context.Context, database string, nameOrAlias string) (string, error) {
|
||||
ret := _m.Called(ctx, database, nameOrAlias)
|
||||
|
||||
@@ -339,6 +339,10 @@ func (node *Proxy) Stop() error {
|
||||
node.resourceManager.Close()
|
||||
}
|
||||
|
||||
if globalMetaCache != nil {
|
||||
globalMetaCache.Close()
|
||||
}
|
||||
|
||||
node.cancel()
|
||||
node.wg.Wait()
|
||||
|
||||
|
||||
@@ -678,6 +678,10 @@ func (coord *MixCoordMock) CreatePartition(ctx context.Context, req *milvuspb.Cr
|
||||
return merr.Success(), nil
|
||||
}
|
||||
|
||||
func (coord *MixCoordMock) CreatePartitionV2(ctx context.Context, req *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
return &rootcoordpb.CreatePartitionResponse{}, nil
|
||||
}
|
||||
|
||||
func (coord *MixCoordMock) DropPartition(ctx context.Context, req *milvuspb.DropPartitionRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||
code := coord.state.Load().(commonpb.StateCode)
|
||||
if code != commonpb.StateCode_Healthy {
|
||||
|
||||
@@ -2224,7 +2224,11 @@ func (t *createPartitionTask) PreExecute(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (t *createPartitionTask) Execute(ctx context.Context) (err error) {
|
||||
t.result, err = t.mixCoord.CreatePartition(ctx, t.CreatePartitionRequest)
|
||||
resp, err := t.mixCoord.CreatePartitionV2(ctx, t.CreatePartitionRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.result = resp.Status
|
||||
if err := merr.CheckRPCCall(t.result, err); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -2233,15 +2237,10 @@ func (t *createPartitionTask) Execute(ctx context.Context) (err error) {
|
||||
t.result = merr.Status(err)
|
||||
return err
|
||||
}
|
||||
partitionID, err := globalMetaCache.GetPartitionID(ctx, t.GetDbName(), t.GetCollectionName(), t.GetPartitionName())
|
||||
if err != nil {
|
||||
t.result = merr.Status(err)
|
||||
return err
|
||||
}
|
||||
t.result, err = t.mixCoord.SyncNewCreatedPartition(ctx, &querypb.SyncNewCreatedPartitionRequest{
|
||||
Base: commonpbutil.NewMsgBase(commonpbutil.WithMsgType(commonpb.MsgType_ReleasePartitions)),
|
||||
Base: commonpbutil.NewMsgBase(commonpbutil.WithMsgType(commonpb.MsgType_CreatePartition)),
|
||||
CollectionID: collectionID,
|
||||
PartitionID: partitionID,
|
||||
PartitionID: resp.PartitionID,
|
||||
})
|
||||
return merr.CheckRPCCall(t.result, err)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import (
|
||||
"github.com/milvus-io/milvus/pkg/v3/proto/indexpb"
|
||||
"github.com/milvus-io/milvus/pkg/v3/proto/internalpb"
|
||||
"github.com/milvus-io/milvus/pkg/v3/proto/querypb"
|
||||
"github.com/milvus-io/milvus/pkg/v3/proto/rootcoordpb"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/commonpbutil"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/funcutil"
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/merr"
|
||||
@@ -2491,8 +2492,10 @@ func TestCreatePartitionTask(t *testing.T) {
|
||||
|
||||
// setup global meta cache
|
||||
mockCache.EXPECT().GetCollectionID(mock.Anything, mock.Anything, mock.Anything).Return(100, nil).Once()
|
||||
mockCache.EXPECT().GetPartitionID(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(1000, nil).Once()
|
||||
rc.EXPECT().CreatePartition(mock.Anything, mock.Anything).Return(merr.Success(), nil).Once()
|
||||
rc.EXPECT().CreatePartitionV2(mock.Anything, mock.Anything).Return(&rootcoordpb.CreatePartitionResponse{
|
||||
Status: merr.Success(),
|
||||
PartitionID: 1000,
|
||||
}, nil).Once()
|
||||
rc.EXPECT().SyncNewCreatedPartition(mock.Anything, mock.Anything).Return(merr.Success(), nil).Once()
|
||||
err := task.Execute(ctx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
// Licensed to the LF AI & Data foundation under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proxy
|
||||
|
||||
import "sync"
|
||||
|
||||
// EntryState represents the state of a cache entry.
|
||||
type EntryState int
|
||||
|
||||
const (
|
||||
EntryStateActive EntryState = iota
|
||||
EntryStateStale
|
||||
)
|
||||
|
||||
type VersionTable[K comparable, V any] struct {
|
||||
entries map[K]*VersionEntry[K, V]
|
||||
}
|
||||
|
||||
func NewVersionTable[K comparable, V any]() *VersionTable[K, V] {
|
||||
return &VersionTable[K, V]{
|
||||
entries: make(map[K]*VersionEntry[K, V]),
|
||||
}
|
||||
}
|
||||
|
||||
type VersionEntry[key comparable, V any] struct {
|
||||
key key
|
||||
value V
|
||||
version uint64
|
||||
state EntryState
|
||||
}
|
||||
|
||||
func (t *VersionTable[K, V]) Lookup(key K) (*VersionEntry[K, V], bool) {
|
||||
entry, ok := t.entries[key]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return entry, true
|
||||
}
|
||||
|
||||
func (t *VersionTable[K, V]) Insert(key K, value V, version uint64) *VersionEntry[K, V] {
|
||||
entry, ok := t.entries[key]
|
||||
if !ok || version > entry.version {
|
||||
newEntry := &VersionEntry[K, V]{
|
||||
key: key,
|
||||
value: value,
|
||||
version: version,
|
||||
state: EntryStateActive,
|
||||
}
|
||||
t.entries[key] = newEntry
|
||||
return newEntry
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
func (t *VersionTable[K, V]) Erase(key K) {
|
||||
delete(t.entries, key)
|
||||
}
|
||||
|
||||
func (t *VersionTable[K, V]) Stale(key K, version uint64) {
|
||||
var v V
|
||||
newEntry := &VersionEntry[K, V]{
|
||||
key: key,
|
||||
value: v,
|
||||
version: version,
|
||||
state: EntryStateStale,
|
||||
}
|
||||
t.entries[key] = newEntry
|
||||
}
|
||||
|
||||
type RefCount[K comparable] map[K]uint64
|
||||
|
||||
func (r RefCount[K]) Inc(key K) {
|
||||
r[key]++
|
||||
}
|
||||
|
||||
func (r RefCount[K]) Dec(key K) {
|
||||
r[key]--
|
||||
}
|
||||
|
||||
func (r RefCount[K]) Count(key K) uint64 {
|
||||
return r[key]
|
||||
}
|
||||
|
||||
func (r RefCount[K]) Erase(key K) {
|
||||
delete(r, key)
|
||||
}
|
||||
|
||||
type VersionCache[K comparable, V any] struct {
|
||||
sync.Mutex
|
||||
table *VersionTable[K, V]
|
||||
refs RefCount[K]
|
||||
}
|
||||
|
||||
// ReleaseFunc is a function that releases the entry.
|
||||
type ReleaseFunc[K comparable, V any] func(entry *VersionEntry[K, V])
|
||||
|
||||
// Lookup returns the entry if it exists and increments the reference count.
|
||||
// Caller must call Release to decrement the reference count after using the entry.
|
||||
func (c *VersionCache[K, V]) Lookup(key K) (*VersionEntry[K, V], bool, ReleaseFunc[K, V]) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
entry, ok := c.table.Lookup(key)
|
||||
if ok {
|
||||
c.refs.Inc(key)
|
||||
return entry, true, c.Release
|
||||
}
|
||||
|
||||
return nil, false, c.Release
|
||||
}
|
||||
|
||||
// Insert inserts a new entry into the cache and increments the reference count.
|
||||
// Caller must call Release to decrement the reference count after using the entry.
|
||||
func (c *VersionCache[K, V]) Insert(key K, value V, version uint64) (*VersionEntry[K, V], ReleaseFunc[K, V]) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
entry := c.table.Insert(key, value, version)
|
||||
c.refs.Inc(key)
|
||||
return entry, c.Release
|
||||
}
|
||||
|
||||
func (c *VersionCache[K, V]) InsertBatchWithoutRef(keys []K, values []V, versions []uint64) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for i := range keys {
|
||||
c.table.Insert(keys[i], values[i], versions[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Release decrements the reference count for the entry
|
||||
// Users should always call this function to decrement the reference count after using the entry.
|
||||
func (c *VersionCache[K, V]) Release(entry *VersionEntry[K, V]) {
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.refs.Dec(entry.key)
|
||||
}
|
||||
|
||||
// Stale marks the entry as stale or erases it if the reference count is 0.
|
||||
func (c *VersionCache[K, V]) Stale(key K, version uint64) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
if c.refs.Count(key) == 0 {
|
||||
c.table.Erase(key)
|
||||
c.refs.Erase(key)
|
||||
} else {
|
||||
c.table.Stale(key, version)
|
||||
}
|
||||
}
|
||||
|
||||
// StaleIf marks entries as stale or erases them based on the predicate.
|
||||
// For each entry where predicate returns true:
|
||||
// - if ref count is 0, erase it directly
|
||||
// - otherwise, mark it as Stale
|
||||
func (c *VersionCache[K, V]) StaleIf(predicate func(K) bool, version uint64) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for key := range c.table.entries {
|
||||
if predicate(key) {
|
||||
if c.refs.Count(key) == 0 {
|
||||
c.table.Erase(key)
|
||||
c.refs.Erase(key)
|
||||
} else {
|
||||
c.table.Stale(key, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prune erases all entries that are stale and have reference count 0.
|
||||
// Users should call this function if they care about memory usage.
|
||||
func (c *VersionCache[K, V]) Prune() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for key, entry := range c.table.entries {
|
||||
if c.refs.Count(key) == 0 && entry.state == EntryStateStale {
|
||||
c.table.Erase(key)
|
||||
c.refs.Erase(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewVersionCache[K comparable, V any]() *VersionCache[K, V] {
|
||||
return &VersionCache[K, V]{
|
||||
table: NewVersionTable[K, V](),
|
||||
refs: make(RefCount[K]),
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,6 @@ func (job *SyncNewCreatedPartitionJob) Execute() error {
|
||||
if collection == nil || collection.GetLoadType() == querypb.LoadType_LoadPartition {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if partition already existed
|
||||
if partition := job.meta.GetPartition(job.ctx, job.req.GetPartitionID()); partition != nil {
|
||||
return nil
|
||||
@@ -96,5 +95,5 @@ func (job *SyncNewCreatedPartitionJob) Execute() error {
|
||||
return errors.Wrap(err, msg)
|
||||
}
|
||||
|
||||
return WaitCurrentTargetUpdated(job.ctx, job.targetObserver, job.req.GetCollectionID())
|
||||
return WaitUpdatePartition(job.ctx, job.targetObserver, job.req.GetCollectionID(), job.req.GetPartitionID())
|
||||
}
|
||||
|
||||
@@ -104,6 +104,33 @@ func WaitCurrentTargetUpdated(ctx context.Context, targetObserver *observers.Tar
|
||||
|
||||
// accelerate check
|
||||
targetObserver.TriggerUpdateCurrentTarget(collection)
|
||||
|
||||
// wait current target ready
|
||||
select {
|
||||
case <-ready:
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return errors.Wrapf(ctx.Err(), "context error while waiting for current target updated, collection=%d", collection)
|
||||
case <-time.After(waitCollectionReleasedTimeout):
|
||||
return errors.Errorf("wait current target updated timeout, collection=%d", collection)
|
||||
}
|
||||
}
|
||||
|
||||
func WaitUpdatePartition(ctx context.Context, targetObserver *observers.TargetObserver, collection int64, partition int64) error {
|
||||
// manual trigger update next target
|
||||
ready, err := targetObserver.UpdatePartition(collection, partition)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to update next target, collection=%d", collection)
|
||||
}
|
||||
select {
|
||||
case <-ready:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// accelerate check
|
||||
targetObserver.TriggerUpdateCurrentTarget(collection)
|
||||
|
||||
// wait current target ready
|
||||
select {
|
||||
case <-ready:
|
||||
|
||||
@@ -436,6 +436,13 @@ func (m *CollectionManager) GetAllPartitions(ctx context.Context) []*Partition {
|
||||
return lo.Values(m.partitions)
|
||||
}
|
||||
|
||||
func (m *CollectionManager) GetPartitionIDsByCollection(ctx context.Context, collectionID typeutil.UniqueID) []int64 {
|
||||
m.rwmutex.RLock()
|
||||
defer m.rwmutex.RUnlock()
|
||||
|
||||
return m.collectionPartitions[collectionID].Collect()
|
||||
}
|
||||
|
||||
func (m *CollectionManager) GetPartitionsByCollection(ctx context.Context, collectionID typeutil.UniqueID) []*Partition {
|
||||
m.rwmutex.RLock()
|
||||
defer m.rwmutex.RUnlock()
|
||||
@@ -503,6 +510,13 @@ func (m *CollectionManager) PutPartitionWithoutSave(ctx context.Context, partiti
|
||||
}
|
||||
|
||||
func (m *CollectionManager) putPartition(ctx context.Context, partitions []*Partition, withSave bool) error {
|
||||
// Check all partitions' collections exist (prevent orphan partitions)
|
||||
for _, partition := range partitions {
|
||||
if _, ok := m.collections[partition.GetCollectionID()]; !ok {
|
||||
return merr.WrapErrCollectionNotLoaded(partition.GetCollectionID())
|
||||
}
|
||||
}
|
||||
|
||||
if withSave {
|
||||
loadInfos := lo.Map(partitions, func(partition *Partition, _ int) *querypb.PartitionLoadInfo {
|
||||
return partition.PartitionLoadInfo
|
||||
|
||||
@@ -153,15 +153,14 @@ func (mgr *TargetManager) UpdateCollectionNextTarget(ctx context.Context, collec
|
||||
return err
|
||||
}
|
||||
|
||||
partitions := mgr.meta.GetPartitionsByCollection(ctx, collectionID)
|
||||
partitionIDs := lo.Map(partitions, func(partition *Partition, i int) int64 {
|
||||
return partition.PartitionID
|
||||
})
|
||||
|
||||
segments := make(map[int64]*datapb.SegmentInfo, 0)
|
||||
partitionSet := typeutil.NewUniqueSet(partitionIDs...)
|
||||
partitionIDs := mgr.meta.GetPartitionIDsByCollection(ctx, collectionID)
|
||||
segments := make(map[int64]*datapb.SegmentInfo, len(segmentInfos))
|
||||
partitionSet := make(map[int64]struct{}, len(partitionIDs))
|
||||
for _, partitionID := range partitionIDs {
|
||||
partitionSet[partitionID] = struct{}{}
|
||||
}
|
||||
for _, segmentInfo := range segmentInfos {
|
||||
if partitionSet.Contain(segmentInfo.GetPartitionID()) || segmentInfo.GetPartitionID() == common.AllPartitionsID {
|
||||
if _, ok := partitionSet[segmentInfo.GetPartitionID()]; ok || segmentInfo.GetPartitionID() == common.AllPartitionsID {
|
||||
segments[segmentInfo.GetID()] = segmentInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ const (
|
||||
UpdateCollection targetOp = iota + 1
|
||||
ReleaseCollection
|
||||
ReleasePartition
|
||||
UpdatePartition
|
||||
)
|
||||
|
||||
type targetUpdateRequest struct {
|
||||
@@ -243,6 +244,39 @@ func (ob *TargetObserver) schedule(ctx context.Context) {
|
||||
ob.targetMgr.RemovePartitionFromNextTarget(ctx, req.CollectionID, req.PartitionIDs...)
|
||||
ob.keylocks.Unlock(req.CollectionID)
|
||||
req.Notifier <- nil
|
||||
case UpdatePartition:
|
||||
// Fast path: check with read lock first
|
||||
ob.keylocks.RLock(req.CollectionID)
|
||||
exists := ob.targetMgr.IsCurrentTargetExist(ctx, req.CollectionID, req.PartitionIDs[0])
|
||||
ob.keylocks.RUnlock(req.CollectionID)
|
||||
|
||||
if exists {
|
||||
close(req.ReadyNotifier)
|
||||
req.Notifier <- nil
|
||||
} else {
|
||||
// Slow path: need to update next target
|
||||
ob.keylocks.Lock(req.CollectionID)
|
||||
// Double check after acquiring write lock
|
||||
if ob.targetMgr.IsCurrentTargetExist(ctx, req.CollectionID, req.PartitionIDs[0]) {
|
||||
close(req.ReadyNotifier)
|
||||
req.Notifier <- nil
|
||||
} else {
|
||||
err := ob.updateNextTarget(ctx, req.CollectionID)
|
||||
if err != nil {
|
||||
log.Warn("failed to manually update next target",
|
||||
zap.Int64("collectionID", req.CollectionID),
|
||||
zap.String("opType", req.opType.String()),
|
||||
zap.Error(err))
|
||||
close(req.ReadyNotifier)
|
||||
} else {
|
||||
ob.mut.Lock()
|
||||
ob.readyNotifiers[req.CollectionID] = append(ob.readyNotifiers[req.CollectionID], req.ReadyNotifier)
|
||||
ob.mut.Unlock()
|
||||
}
|
||||
req.Notifier <- err
|
||||
}
|
||||
ob.keylocks.Unlock(req.CollectionID)
|
||||
}
|
||||
}
|
||||
log.Info("manually trigger update target done",
|
||||
zap.Int64("collectionID", req.CollectionID),
|
||||
@@ -324,6 +358,20 @@ func (ob *TargetObserver) UpdateNextTarget(collectionID int64) (chan struct{}, e
|
||||
return readyCh, <-notifier
|
||||
}
|
||||
|
||||
func (ob *TargetObserver) UpdatePartition(collectionID int64, partitionID int64) (chan struct{}, error) {
|
||||
notifier := make(chan error)
|
||||
readyCh := make(chan struct{})
|
||||
defer close(notifier)
|
||||
ob.updateChan <- targetUpdateRequest{
|
||||
CollectionID: collectionID,
|
||||
PartitionIDs: []int64{partitionID},
|
||||
opType: UpdatePartition,
|
||||
Notifier: notifier,
|
||||
ReadyNotifier: readyCh,
|
||||
}
|
||||
return readyCh, <-notifier
|
||||
}
|
||||
|
||||
func (ob *TargetObserver) ReleaseCollection(collectionID int64) {
|
||||
notifier := make(chan error)
|
||||
defer close(notifier)
|
||||
|
||||
@@ -482,7 +482,25 @@ func (s *Server) SyncNewCreatedPartition(ctx context.Context, req *querypb.SyncN
|
||||
}
|
||||
|
||||
syncJob := job.NewSyncNewCreatedPartitionJob(ctx, req, s.meta, s.broker, s.targetObserver, s.targetMgr)
|
||||
s.jobScheduler.Add(syncJob)
|
||||
go func() {
|
||||
defer func() {
|
||||
syncJob.PostExecute()
|
||||
syncJob.Done()
|
||||
}()
|
||||
|
||||
err := syncJob.PreExecute()
|
||||
if err != nil {
|
||||
log.Warn(failedMsg, zap.Error(err))
|
||||
syncJob.SetError(err)
|
||||
return
|
||||
}
|
||||
err = syncJob.Execute()
|
||||
if err != nil {
|
||||
log.Warn(failedMsg, zap.Error(err))
|
||||
syncJob.SetError(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
err := syncJob.Wait()
|
||||
if err != nil {
|
||||
log.Warn(failedMsg, zap.Error(err))
|
||||
|
||||
@@ -629,7 +629,7 @@ func (t *createCollectionTask) validateIfCollectionExists(ctx context.Context) e
|
||||
}
|
||||
|
||||
// Check if the collection already exists.
|
||||
existedCollInfo, err := t.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
existedCollInfo, err := t.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err == nil {
|
||||
newCollInfo := newCollectionModel(t.header, t.body, 0)
|
||||
if equal := existedCollInfo.Equal(*newCollInfo); !equal {
|
||||
|
||||
@@ -1649,7 +1649,7 @@ func Test_createCollectionTask_Prepare(t *testing.T) {
|
||||
}, nil)
|
||||
meta.EXPECT().GetGeneralCount(mock.Anything).Return(0)
|
||||
meta.EXPECT().DescribeAlias(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", errors.New("not found"))
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("not found"))
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("not found"))
|
||||
|
||||
paramtable.Get().Save(Params.QuotaConfig.MaxCollectionNum.Key, strconv.Itoa(math.MaxInt64))
|
||||
defer paramtable.Get().Reset(Params.QuotaConfig.MaxCollectionNum.Key)
|
||||
@@ -1742,7 +1742,7 @@ func TestCreateCollectionTask_Prepare_WithProperty(t *testing.T) {
|
||||
}).Once()
|
||||
meta.EXPECT().GetGeneralCount(mock.Anything).Return(0).Once()
|
||||
meta.EXPECT().DescribeAlias(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", errors.New("not found"))
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("not found"))
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("not found"))
|
||||
defer cleanTestEnv()
|
||||
|
||||
collectionName := funcutil.GenRandomStr()
|
||||
@@ -1805,7 +1805,7 @@ func Test_createCollectionTask_PartitionKey(t *testing.T) {
|
||||
}, nil)
|
||||
meta.EXPECT().GetGeneralCount(mock.Anything).Return(0)
|
||||
meta.EXPECT().DescribeAlias(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", errors.New("not found"))
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("not found"))
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("not found"))
|
||||
|
||||
paramtable.Get().Save(Params.QuotaConfig.MaxCollectionNum.Key, strconv.Itoa(math.MaxInt64))
|
||||
defer paramtable.Get().Reset(Params.QuotaConfig.MaxCollectionNum.Key)
|
||||
|
||||
@@ -175,7 +175,7 @@ func (*Core) startBroadcastWithCollectionLock(ctx context.Context, dbName string
|
||||
// Some API like AlterCollection can be called with alias or collection name,
|
||||
// so we need to get the real collection name to add resource key lock.
|
||||
func (c *Core) startBroadcastWithAliasOrCollectionLock(ctx context.Context, dbName string, collectionNameOrAlias string) (broadcaster.BroadcastAPI, error) {
|
||||
coll, err := c.meta.GetCollectionByName(ctx, dbName, collectionNameOrAlias, typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, dbName, collectionNameOrAlias, typeutil.MaxTimestamp, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get collection by name")
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func TestDDLCallbacksAliasDDL(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(status, err))
|
||||
|
||||
coll, err := core.meta.GetCollectionByName(context.Background(), "test", "test_alias", typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(context.Background(), "test", "test_alias", typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, coll.CollectionID)
|
||||
require.Equal(t, "test_collection", coll.Name)
|
||||
|
||||
@@ -46,7 +46,7 @@ func (c *Core) broadcastCreateAlias(ctx context.Context, req *milvuspb.CreateAli
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
collection, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
collection, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (c *Core) broadcastAlterAlias(ctx context.Context, req *milvuspb.AlterAlias
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
collection, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
collection, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func (c *Core) broadcastAlterCollectionForAddField(ctx context.Context, req *mil
|
||||
defer broadcaster.Close()
|
||||
|
||||
// check if the collection is created.
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ func getFieldSchema(fieldName string) []byte {
|
||||
}
|
||||
|
||||
func assertFieldExists(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, fieldName string, fieldID int64) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
for _, field := range coll.Fields {
|
||||
if field.Name == fieldName {
|
||||
|
||||
@@ -21,7 +21,7 @@ func (c *Core) broadcastAlterCollectionV2ForAlterCollectionField(ctx context.Con
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func TestDDLCallbacksAlterCollectionField(t *testing.T) {
|
||||
}
|
||||
|
||||
func assertFieldPropertiesNotFound(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, fieldName string, key string) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
for _, field := range coll.Fields {
|
||||
if field.Name == fieldName {
|
||||
@@ -140,7 +140,7 @@ func assertFieldPropertiesNotFound(t *testing.T, ctx context.Context, core *Core
|
||||
}
|
||||
|
||||
func assertFieldProperties(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, fieldName string, key string, val string) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
for _, field := range coll.Fields {
|
||||
if field.Name == fieldName {
|
||||
|
||||
@@ -57,7 +57,7 @@ func (c *Core) broadcastAlterCollectionForRenameCollection(ctx context.Context,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetOldName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetOldName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -80,10 +80,10 @@ func TestDDLCallbacksAlterCollectionName(t *testing.T) {
|
||||
NewName: newCollectionName,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(resp, err))
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
require.Nil(t, coll)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, newCollectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, newCollectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, coll)
|
||||
require.Equal(t, coll.Name, newCollectionName)
|
||||
@@ -100,10 +100,10 @@ func TestDDLCallbacksAlterCollectionName(t *testing.T) {
|
||||
NewName: newCollectionName,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(resp, err))
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, newCollectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, newCollectionName, typeutil.MaxTimestamp, false)
|
||||
require.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
require.Nil(t, coll)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, newDbName, newCollectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, newDbName, newCollectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, coll)
|
||||
require.Equal(t, coll.Name, newCollectionName)
|
||||
@@ -117,16 +117,16 @@ func TestDDLCallbacksAlterCollectionName(t *testing.T) {
|
||||
NewName: collectionName,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(resp, err))
|
||||
coll, err = core.meta.GetCollectionByName(ctx, newDbName, newCollectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, newDbName, newCollectionName, typeutil.MaxTimestamp, false)
|
||||
require.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
require.Nil(t, coll)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, newDbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, newDbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
require.Nil(t, coll)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, newCollectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, newCollectionName, typeutil.MaxTimestamp, false)
|
||||
require.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
require.Nil(t, coll)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, coll)
|
||||
require.Equal(t, coll.Name, collectionName)
|
||||
|
||||
@@ -71,7 +71,7 @@ func (c *Core) broadcastAlterCollectionForAlterCollection(ctx context.Context, r
|
||||
defer broadcaster.Close()
|
||||
|
||||
// check if the collection exists
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func (c *Core) broadcastAlterCollectionForAlterDynamicField(ctx context.Context,
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -279,7 +279,7 @@ func (c *Core) broadcastAlterCollectionForAlterDynamicField(ctx context.Context,
|
||||
|
||||
// getCacheExpireForCollection gets the cache expirations for collection.
|
||||
func (c *Core) getCacheExpireForCollection(ctx context.Context, dbName string, collectionNameOrAlias string) (*message.CacheExpirations, error) {
|
||||
coll, err := c.meta.GetCollectionByName(ctx, dbName, collectionNameOrAlias, typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, dbName, collectionNameOrAlias, typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -552,13 +552,13 @@ func TestDDLCallbacksAlterCollectionProperties_TTLFieldPreservesExternalSpec(t *
|
||||
}
|
||||
|
||||
func assertExternalSource(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, expectedSource string) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedSource, coll.ExternalSource)
|
||||
}
|
||||
|
||||
func assertExternalSpec(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, expectedSpec string) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedSpec, coll.ExternalSpec)
|
||||
}
|
||||
@@ -745,7 +745,7 @@ func createCollectionAndAliasForTest(t *testing.T, ctx context.Context, core *Co
|
||||
}
|
||||
|
||||
func assertReplicaNumber(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, replicaNumber int64) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
replicaNum, err := common.CollectionLevelReplicaNumber(coll.Properties)
|
||||
if replicaNumber == 0 {
|
||||
@@ -757,7 +757,7 @@ func assertReplicaNumber(t *testing.T, ctx context.Context, core *Core, dbName s
|
||||
}
|
||||
|
||||
func assertResourceGroups(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, resourceGroups []string) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
rgs, err := common.CollectionLevelResourceGroups(coll.Properties)
|
||||
if len(resourceGroups) == 0 {
|
||||
@@ -769,25 +769,25 @@ func assertResourceGroups(t *testing.T, ctx context.Context, core *Core, dbName
|
||||
}
|
||||
|
||||
func assertConsistencyLevel(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, consistencyLevel commonpb.ConsistencyLevel) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, consistencyLevel, coll.ConsistencyLevel)
|
||||
}
|
||||
|
||||
func assertDescription(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, description string) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, description, coll.Description)
|
||||
}
|
||||
|
||||
func assertSchemaVersion(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, schemaVersion int32) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, schemaVersion, coll.SchemaVersion)
|
||||
}
|
||||
|
||||
func assertDynamicSchema(t *testing.T, ctx context.Context, core *Core, dbName string, collectionName string, dynamicSchema bool) {
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dynamicSchema, coll.EnableDynamicField)
|
||||
if !dynamicSchema {
|
||||
|
||||
@@ -38,7 +38,7 @@ func (c *Core) broadcastAlterCollectionSchema(ctx context.Context, req *milvuspb
|
||||
return err
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func (c *Core) broadcastAlterCollectionForAlterFunction(ctx context.Context, req
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
oldColl, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
oldColl, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func (c *Core) broadcastAlterCollectionForDropFunction(ctx context.Context, req
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
oldColl, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
oldColl, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -212,7 +212,7 @@ func (c *Core) broadcastAlterCollectionForAddFunction(ctx context.Context, req *
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
oldColl, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
oldColl, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestCallAlterCollection_Su
|
||||
core.meta = mockMeta
|
||||
|
||||
// Mock meta calls for getCacheExpireForCollection
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
mockMeta.EXPECT().ListAliases(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp).Return([]string{}, nil)
|
||||
|
||||
// Mock broadcaster
|
||||
@@ -167,7 +167,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestCallAlterCollection_Ge
|
||||
core.meta = mockMeta
|
||||
|
||||
// Mock meta calls to return error
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp).Return(nil, errors.New("cache expire error"))
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp, mock.Anything).Return(nil, errors.New("cache expire error"))
|
||||
|
||||
err := callAlterCollection(ctx, core, suite.mockBroadcaster, coll, dbName, collectionName)
|
||||
suite.Error(err)
|
||||
@@ -186,7 +186,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestCallAlterCollection_Br
|
||||
core.meta = mockMeta
|
||||
|
||||
// Mock meta calls for getCacheExpireForCollection
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
mockMeta.EXPECT().ListAliases(mock.Anything, dbName, collectionName, typeutil.MaxTimestamp).Return([]string{}, nil)
|
||||
|
||||
// Mock broadcaster to return error
|
||||
@@ -460,7 +460,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestBroadcastAlterCollecti
|
||||
coll := suite.createTestCollection()
|
||||
|
||||
mockMeta := mockrootcoord.NewIMetaTable(suite.T())
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
suite.core.meta = mockMeta
|
||||
|
||||
req := &milvuspb.AlterCollectionFunctionRequest{
|
||||
@@ -486,7 +486,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestBroadcastAlterCollecti
|
||||
coll := suite.createTestCollection()
|
||||
|
||||
mockMeta := mockrootcoord.NewIMetaTable(suite.T())
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
suite.core.meta = mockMeta
|
||||
|
||||
req := &milvuspb.DropCollectionFunctionRequest{
|
||||
@@ -522,7 +522,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestBroadcastAlterCollecti
|
||||
coll := suite.createTestCollection()
|
||||
|
||||
mockMeta := mockrootcoord.NewIMetaTable(suite.T())
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
suite.core.meta = mockMeta
|
||||
|
||||
req := &milvuspb.AddCollectionFunctionRequest{
|
||||
@@ -546,7 +546,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestBroadcastAlterCollecti
|
||||
suite.Run("function input field not exists", func() {
|
||||
coll := suite.createTestCollection()
|
||||
mockMeta := mockrootcoord.NewIMetaTable(suite.T())
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
suite.core.meta = mockMeta
|
||||
|
||||
req := &milvuspb.AddCollectionFunctionRequest{
|
||||
@@ -572,7 +572,7 @@ func (suite *DDLCallbacksCollectionFunctionTestSuite) TestBroadcastAlterCollecti
|
||||
coll := suite.createTestCollection()
|
||||
|
||||
mockMeta := mockrootcoord.NewIMetaTable(suite.T())
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp).Return(coll, nil)
|
||||
mockMeta.EXPECT().GetCollectionByName(mock.Anything, "test_db", "test_collection", typeutil.MaxTimestamp, mock.Anything).Return(coll, nil)
|
||||
suite.core.meta = mockMeta
|
||||
|
||||
req := &milvuspb.AddCollectionFunctionRequest{
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestDDLCallbacksCollectionDDL(t *testing.T) {
|
||||
Schema: schemaBytes,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(status, err))
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, coll.Name, collectionName)
|
||||
// create a collection with same schema should be idempotent.
|
||||
@@ -105,7 +105,7 @@ func TestDDLCallbacksCollectionDDL(t *testing.T) {
|
||||
PartitionName: partitionName,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(status, err))
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, coll.Partitions, 2)
|
||||
require.Contains(t, lo.Map(coll.Partitions, func(p *model.Partition, _ int) string { return p.PartitionName }), partitionName)
|
||||
@@ -116,7 +116,7 @@ func TestDDLCallbacksCollectionDDL(t *testing.T) {
|
||||
PartitionName: partitionName,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(status, err))
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, coll.Partitions, 2)
|
||||
|
||||
@@ -148,7 +148,7 @@ func TestDDLCallbacksCollectionDDL(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(resp.GetStatus(), err))
|
||||
// verify collection still exists after truncate
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, coll.Name, collectionName)
|
||||
require.Equal(t, 1, len(coll.ShardInfos))
|
||||
@@ -163,7 +163,7 @@ func TestDDLCallbacksCollectionDDL(t *testing.T) {
|
||||
CollectionName: collectionName,
|
||||
})
|
||||
require.NoError(t, merr.CheckRPCCall(status, err))
|
||||
_, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp)
|
||||
_, err = core.meta.GetCollectionByName(ctx, dbName, collectionName, typeutil.MaxTimestamp, false)
|
||||
require.Error(t, err)
|
||||
// drop a dropped collection should be idempotent.
|
||||
status, err = core.DropCollection(ctx, &milvuspb.DropCollectionRequest{
|
||||
|
||||
@@ -33,35 +33,33 @@ import (
|
||||
"github.com/milvus-io/milvus/pkg/v3/util/typeutil"
|
||||
)
|
||||
|
||||
func (c *Core) broadcastCreatePartition(ctx context.Context, in *milvuspb.CreatePartitionRequest) error {
|
||||
func (c *Core) broadcastCreatePartition(ctx context.Context, in *milvuspb.CreatePartitionRequest) (int64, error) {
|
||||
broadcaster, err := c.startBroadcastWithAliasOrCollectionLock(ctx, in.GetDbName(), in.GetCollectionName())
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
collMeta, err := c.meta.GetCollectionByName(ctx, in.GetDbName(), in.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
collMeta, err := c.meta.GetCollectionByName(ctx, in.GetDbName(), in.GetCollectionName(), typeutil.MaxTimestamp, true)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
if err := checkGeneralCapacity(ctx, 0, 1, 0, c); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
// idempotency check here.
|
||||
for _, partition := range collMeta.Partitions {
|
||||
if partition.PartitionName == in.GetPartitionName() {
|
||||
return errIgnoerdCreatePartition
|
||||
}
|
||||
// idempotency check using partition name index (O(1) instead of O(n))
|
||||
if partitionID, exists := c.meta.GetPartitionIDByName(collMeta.CollectionID, in.GetPartitionName()); exists {
|
||||
return partitionID, errIgnoerdCreatePartition
|
||||
}
|
||||
cfgMaxPartitionNum := Params.RootCoordCfg.MaxPartitionNum.GetAsInt()
|
||||
if len(collMeta.Partitions) >= cfgMaxPartitionNum {
|
||||
return fmt.Errorf("partition number (%d) exceeds max configuration (%d), collection: %s",
|
||||
return 0, fmt.Errorf("partition number (%d) exceeds max configuration (%d), collection: %s",
|
||||
len(collMeta.Partitions), cfgMaxPartitionNum, collMeta.Name)
|
||||
}
|
||||
|
||||
partID, err := c.idAllocator.AllocOne()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to allocate partition ID")
|
||||
return 0, errors.Wrap(err, "failed to allocate partition ID")
|
||||
}
|
||||
|
||||
channels := make([]string, 0, collMeta.ShardsNum+1)
|
||||
@@ -86,7 +84,7 @@ func (c *Core) broadcastCreatePartition(ctx context.Context, in *milvuspb.Create
|
||||
WithBroadcast(channels).
|
||||
MustBuildBroadcast()
|
||||
_, err = broadcaster.Broadcast(ctx, msg)
|
||||
return err
|
||||
return partID, err
|
||||
}
|
||||
|
||||
func (c *DDLCallback) createPartitionV1AckCallback(ctx context.Context, result message.BroadcastResultCreatePartitionMessageV1) error {
|
||||
|
||||
@@ -43,7 +43,7 @@ func (c *Core) broadcastDropPartition(ctx context.Context, in *milvuspb.DropPart
|
||||
}
|
||||
defer broadcaster.Close()
|
||||
|
||||
collMeta, err := c.meta.GetCollectionByName(ctx, in.GetDbName(), in.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
collMeta, err := c.meta.GetCollectionByName(ctx, in.GetDbName(), in.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
// Is this idempotent?
|
||||
return err
|
||||
|
||||
@@ -38,7 +38,7 @@ func (c *Core) broadcastTruncateCollection(ctx context.Context, req *milvuspb.Tr
|
||||
defer broadcaster.Close()
|
||||
|
||||
// get collection info
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, req.GetDbName(), req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -185,7 +185,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -213,7 +213,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -244,7 +244,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -277,7 +277,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -298,7 +298,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -332,7 +332,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -361,7 +361,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -417,7 +417,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -470,7 +470,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
ObjectName: util.AnyWord,
|
||||
},
|
||||
}, nil).Once()
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -518,7 +518,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
ObjectName: util.AnyWord,
|
||||
},
|
||||
}, nil).Once()
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -572,7 +572,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
ObjectName: "b",
|
||||
},
|
||||
}, nil).Once()
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
@@ -630,7 +630,7 @@ func TestDescribeCollectionsAuth(t *testing.T) {
|
||||
GroupName: "privilege_group",
|
||||
},
|
||||
}, nil).Once()
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
Name: "test coll",
|
||||
DBID: 1,
|
||||
|
||||
@@ -49,7 +49,7 @@ func (t *dropCollectionTask) validate(ctx context.Context) error {
|
||||
// we cannot handle case that
|
||||
// dropping collection with `ts1` but a collection exists in catalog with newer ts which is bigger than `ts1`.
|
||||
// fortunately, if ddls are promised to execute in sequence, then everything is OK. The `ts1` will always be latest.
|
||||
collMeta, err := t.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
collMeta, err := t.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, merr.ErrCollectionNotFound) || errors.Is(err, merr.ErrDatabaseNotFound) {
|
||||
return errIgnoredDropCollection
|
||||
|
||||
@@ -59,7 +59,7 @@ func Test_dropCollectionTask_Prepare(t *testing.T) {
|
||||
collectionName := funcutil.GenRandomStr()
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
meta.EXPECT().IsAlias(mock.Anything, mock.Anything, mock.Anything).Return(false)
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, merr.ErrCollectionNotFound)
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, merr.ErrCollectionNotFound)
|
||||
core := newTestCore(withMeta(meta))
|
||||
task := &dropCollectionTask{
|
||||
Core: core,
|
||||
@@ -75,7 +75,7 @@ func Test_dropCollectionTask_Prepare(t *testing.T) {
|
||||
collectionName := funcutil.GenRandomStr()
|
||||
meta := mockrootcoord.NewIMetaTable(t)
|
||||
meta.EXPECT().IsAlias(mock.Anything, mock.Anything, mock.Anything).Return(false)
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
DBID: 1,
|
||||
State: pb.CollectionState_CollectionCreated,
|
||||
@@ -102,7 +102,7 @@ func Test_dropCollectionTask_Prepare(t *testing.T) {
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(false)
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
meta.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&model.Collection{
|
||||
CollectionID: 1,
|
||||
DBName: "db1",
|
||||
DBID: 1,
|
||||
|
||||
@@ -43,7 +43,7 @@ func (t *hasCollectionTask) Execute(ctx context.Context) error {
|
||||
t.Rsp.Status = merr.Success()
|
||||
ts := getTravelTs(t.Req)
|
||||
// TODO: what if err != nil && common.IsCollectionNotExistError == false, should we consider this RPC as failure?
|
||||
_, err := t.core.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), ts)
|
||||
_, err := t.core.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), ts, false)
|
||||
t.Rsp.Value = err == nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ func Test_hasCollectionTask_Execute(t *testing.T) {
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(nil, nil)
|
||||
|
||||
core := newTestCore(withMeta(meta))
|
||||
|
||||
@@ -44,7 +44,7 @@ func (t *hasPartitionTask) Execute(ctx context.Context) error {
|
||||
t.Rsp.Status = merr.Success()
|
||||
t.Rsp.Value = false
|
||||
// TODO: why HasPartitionRequest doesn't contain Timestamp but other requests do.
|
||||
coll, err := t.core.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.CollectionName, typeutil.MaxTimestamp)
|
||||
coll, err := t.core.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.CollectionName, typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
t.Rsp.Status = merr.Status(err)
|
||||
return err
|
||||
|
||||
@@ -59,7 +59,7 @@ func Test_hasPartitionTask_Prepare(t *testing.T) {
|
||||
func Test_hasPartitionTask_Execute(t *testing.T) {
|
||||
t.Run("fail to get collection", func(t *testing.T) {
|
||||
metaTable := mockrootcoord.NewIMetaTable(t)
|
||||
metaTable.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, "test coll", mock.Anything).Return(nil, merr.WrapErrCollectionNotFound("test coll"))
|
||||
metaTable.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, "test coll", mock.Anything, mock.Anything).Return(nil, merr.WrapErrCollectionNotFound("test coll"))
|
||||
core := newTestCore(withMeta(metaTable))
|
||||
task := &hasPartitionTask{
|
||||
baseTask: newBaseTask(context.Background(), core),
|
||||
@@ -85,6 +85,7 @@ func Test_hasPartitionTask_Execute(t *testing.T) {
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(&model.Collection{
|
||||
Partitions: []*model.Partition{
|
||||
{
|
||||
@@ -118,6 +119,7 @@ func Test_hasPartitionTask_Execute(t *testing.T) {
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
mock.Anything,
|
||||
).Return(&model.Collection{
|
||||
Partitions: []*model.Partition{
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ type IMetaTable interface {
|
||||
// If the collection does not exist, it will return InvalidCollectionID.
|
||||
// Please use the function with caution.
|
||||
GetCollectionID(ctx context.Context, dbName string, collectionName string) UniqueID
|
||||
GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts Timestamp) (*model.Collection, error)
|
||||
GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts Timestamp, allowUnavailable bool) (*model.Collection, error)
|
||||
GetCollectionByID(ctx context.Context, dbName string, collectionID UniqueID, ts Timestamp, allowUnavailable bool) (*model.Collection, error)
|
||||
GetCollectionByIDWithMaxTs(ctx context.Context, collectionID UniqueID) (*model.Collection, error)
|
||||
ListCollections(ctx context.Context, dbName string, ts Timestamp, onlyAvail bool) ([]*model.Collection, error)
|
||||
@@ -102,6 +102,7 @@ type IMetaTable interface {
|
||||
GetCollectionVirtualChannels(ctx context.Context, colID int64) []string
|
||||
GetPChannelInfo(ctx context.Context, pchannel string) *rootcoordpb.GetPChannelInfoResponse
|
||||
AddPartition(ctx context.Context, partition *model.Partition) error
|
||||
GetPartitionIDByName(collectionID int64, partitionName string) (int64, bool)
|
||||
DropPartition(ctx context.Context, collectionID UniqueID, partitionID UniqueID, ts Timestamp) error
|
||||
RemovePartition(ctx context.Context, collectionID UniqueID, partitionID UniqueID, ts Timestamp) error
|
||||
|
||||
@@ -167,6 +168,9 @@ type MetaTable struct {
|
||||
dbName2Meta map[string]*model.Database // database name -> db meta
|
||||
collID2Meta map[typeutil.UniqueID]*model.Collection // collection id -> collection meta
|
||||
|
||||
// partition name index: collectionID -> partitionName -> partitionID
|
||||
partitionName2ID map[int64]map[string]int64
|
||||
|
||||
fileResourceName2Meta map[string]*internalpb.FileResourceInfo // file resource name -> file resource meta
|
||||
fileResourceID2Meta map[int64]*internalpb.FileResourceInfo // file resource id -> file resource meta
|
||||
fileResourceRefCnt map[int64]int // file resource id -> reference count
|
||||
@@ -202,6 +206,7 @@ func (mt *MetaTable) reload() error {
|
||||
record := timerecord.NewTimeRecorder("rootcoord")
|
||||
mt.dbName2Meta = make(map[string]*model.Database)
|
||||
mt.collID2Meta = make(map[UniqueID]*model.Collection)
|
||||
mt.partitionName2ID = make(map[int64]map[string]int64)
|
||||
mt.fileResourceRefCnt = make(map[int64]int)
|
||||
mt.names = newNameDb()
|
||||
mt.aliases = newNameDb()
|
||||
@@ -262,6 +267,13 @@ func (mt *MetaTable) reload() error {
|
||||
}
|
||||
collection.DBName = dbName // some collections may not have db name or its dbname is not correct, we should fix it here.
|
||||
mt.collID2Meta[collection.CollectionID] = collection
|
||||
// Build partition name index
|
||||
mt.partitionName2ID[collection.CollectionID] = make(map[string]int64)
|
||||
for _, partition := range collection.Partitions {
|
||||
if partition.Available() {
|
||||
mt.partitionName2ID[collection.CollectionID][partition.PartitionName] = partition.PartitionID
|
||||
}
|
||||
}
|
||||
if collection.Available() {
|
||||
mt.names.insert(dbName, collection.Name, collection.CollectionID)
|
||||
for _, fileResourceID := range collection.FileResourceIds {
|
||||
@@ -560,6 +572,13 @@ func (mt *MetaTable) AddCollection(ctx context.Context, coll *model.Collection)
|
||||
|
||||
mt.collID2Meta[coll.CollectionID] = coll.Clone()
|
||||
mt.names.insert(coll.DBName, coll.Name, coll.CollectionID)
|
||||
// Build partition name index for the new collection
|
||||
mt.partitionName2ID[coll.CollectionID] = make(map[string]int64)
|
||||
for _, partition := range coll.Partitions {
|
||||
if partition.Available() {
|
||||
mt.partitionName2ID[coll.CollectionID][partition.PartitionName] = partition.PartitionID
|
||||
}
|
||||
}
|
||||
|
||||
pn := coll.GetPartitionNum(true)
|
||||
mt.generalCnt += pn * int(coll.ShardsNum)
|
||||
@@ -677,6 +696,7 @@ func (mt *MetaTable) removeAllNamesIfMatchedInternal(ctx context.Context, collec
|
||||
|
||||
func (mt *MetaTable) removeCollectionByIDInternal(ctx context.Context, collectionID UniqueID) {
|
||||
delete(mt.collID2Meta, collectionID)
|
||||
delete(mt.partitionName2ID, collectionID)
|
||||
log.Ctx(ctx).Info("delete from collID2Meta",
|
||||
zap.Int64("collectionID", collectionID),
|
||||
)
|
||||
@@ -756,7 +776,7 @@ func (mt *MetaTable) getLatestCollectionByIDInternal(ctx context.Context, collec
|
||||
return nil, merr.WrapErrCollectionNotFound(collectionID)
|
||||
}
|
||||
if allowUnavailable {
|
||||
return coll.Clone(), nil
|
||||
return coll.ShallowClone(), nil
|
||||
}
|
||||
if !coll.Available() {
|
||||
return nil, merr.WrapErrCollectionNotFound(collectionID)
|
||||
@@ -801,7 +821,7 @@ func (mt *MetaTable) getCollectionByIDInternal(ctx context.Context, dbName strin
|
||||
}
|
||||
|
||||
if allowUnavailable {
|
||||
return coll.Clone(), nil
|
||||
return coll.ShallowClone(), nil
|
||||
}
|
||||
|
||||
if !coll.Available() {
|
||||
@@ -811,10 +831,10 @@ func (mt *MetaTable) getCollectionByIDInternal(ctx context.Context, dbName strin
|
||||
return filterUnavailablePartition(coll), nil
|
||||
}
|
||||
|
||||
func (mt *MetaTable) GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts Timestamp) (*model.Collection, error) {
|
||||
func (mt *MetaTable) GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts Timestamp, allowUnavailable bool) (*model.Collection, error) {
|
||||
mt.ddLock.RLock()
|
||||
defer mt.ddLock.RUnlock()
|
||||
return mt.getCollectionByNameInternal(ctx, dbName, collectionName, ts)
|
||||
return mt.getCollectionByNameInternal(ctx, dbName, collectionName, ts, allowUnavailable)
|
||||
}
|
||||
|
||||
// GetCollectionID retrieves the corresponding collectionID based on the collectionName.
|
||||
@@ -849,7 +869,7 @@ func (mt *MetaTable) GetCollectionID(ctx context.Context, dbName string, collect
|
||||
|
||||
// Note: The returned model.Collection is read-only. Do NOT modify it directly,
|
||||
// as it may cause unexpected behavior or inconsistencies.
|
||||
func (mt *MetaTable) getCollectionByNameInternal(ctx context.Context, dbName string, collectionName string, ts Timestamp) (*model.Collection, error) {
|
||||
func (mt *MetaTable) getCollectionByNameInternal(ctx context.Context, dbName string, collectionName string, ts Timestamp, allowUnavailable bool) (*model.Collection, error) {
|
||||
// backward compatibility for rolling upgrade
|
||||
if dbName == "" {
|
||||
log.Ctx(ctx).Warn("db name is empty", zap.String("collectionName", collectionName), zap.Uint64("ts", ts))
|
||||
@@ -863,12 +883,12 @@ func (mt *MetaTable) getCollectionByNameInternal(ctx context.Context, dbName str
|
||||
|
||||
collectionID, ok := mt.aliases.get(dbName, collectionName)
|
||||
if ok {
|
||||
return mt.getCollectionByIDInternal(ctx, dbName, collectionID, ts, false)
|
||||
return mt.getCollectionByIDInternal(ctx, dbName, collectionID, ts, allowUnavailable)
|
||||
}
|
||||
|
||||
collectionID, ok = mt.names.get(dbName, collectionName)
|
||||
if ok {
|
||||
return mt.getCollectionByIDInternal(ctx, dbName, collectionID, ts, false)
|
||||
return mt.getCollectionByIDInternal(ctx, dbName, collectionID, ts, allowUnavailable)
|
||||
}
|
||||
|
||||
if isMaxTs(ts) {
|
||||
@@ -885,6 +905,9 @@ func (mt *MetaTable) getCollectionByNameInternal(ctx context.Context, dbName str
|
||||
if coll == nil || !coll.Available() {
|
||||
return nil, merr.WrapErrCollectionNotFoundWithDB(dbName, collectionName)
|
||||
}
|
||||
if allowUnavailable {
|
||||
return coll.ShallowClone(), nil
|
||||
}
|
||||
return filterUnavailablePartition(coll), nil
|
||||
}
|
||||
|
||||
@@ -1179,7 +1202,7 @@ func (mt *MetaTable) CheckIfCollectionRenamable(ctx context.Context, dbName stri
|
||||
}
|
||||
|
||||
// check new collection already exists
|
||||
coll, err := mt.getCollectionByNameInternal(ctx, newDBName, newName, typeutil.MaxTimestamp)
|
||||
coll, err := mt.getCollectionByNameInternal(ctx, newDBName, newName, typeutil.MaxTimestamp, false)
|
||||
if coll != nil {
|
||||
log.Warn("duplicated new collection name, already taken by another collection or alias.")
|
||||
return fmt.Errorf("duplicated new collection name %s:%s with other collection name or alias", newDBName, newName)
|
||||
@@ -1190,7 +1213,7 @@ func (mt *MetaTable) CheckIfCollectionRenamable(ctx context.Context, dbName stri
|
||||
}
|
||||
|
||||
// get old collection meta
|
||||
oldColl, err := mt.getCollectionByNameInternal(ctx, dbName, oldName, typeutil.MaxTimestamp)
|
||||
oldColl, err := mt.getCollectionByNameInternal(ctx, dbName, oldName, typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
log.Warn("fail to find collection with old name", zap.Error(err))
|
||||
return err
|
||||
@@ -1272,7 +1295,18 @@ func (mt *MetaTable) AddPartition(ctx context.Context, partition *model.Partitio
|
||||
if err := mt.catalog.CreatePartition(ctx, coll.DBID, partition, partition.PartitionCreatedTimestamp); err != nil {
|
||||
return err
|
||||
}
|
||||
mt.collID2Meta[partition.CollectionID].Partitions = append(mt.collID2Meta[partition.CollectionID].Partitions, partition.Clone())
|
||||
// COW: create new slice to avoid data race with concurrent readers
|
||||
oldPartitions := coll.Partitions
|
||||
newPartitions := make([]*model.Partition, len(oldPartitions)+1)
|
||||
copy(newPartitions, oldPartitions)
|
||||
newPartitions[len(oldPartitions)] = partition.Clone()
|
||||
coll.Partitions = newPartitions
|
||||
|
||||
// Update partition name index
|
||||
if mt.partitionName2ID[partition.CollectionID] == nil {
|
||||
mt.partitionName2ID[partition.CollectionID] = make(map[string]int64)
|
||||
}
|
||||
mt.partitionName2ID[partition.CollectionID][partition.PartitionName] = partition.PartitionID
|
||||
|
||||
log.Ctx(ctx).Info("add partition to meta table",
|
||||
zap.Int64("collection", partition.CollectionID), zap.String("partition", partition.PartitionName),
|
||||
@@ -1284,6 +1318,20 @@ func (mt *MetaTable) AddPartition(ctx context.Context, partition *model.Partitio
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPartitionIDByName returns partition ID by collection ID and partition name.
|
||||
// Returns (partitionID, true) if found, (0, false) if not found.
|
||||
func (mt *MetaTable) GetPartitionIDByName(collectionID int64, partitionName string) (int64, bool) {
|
||||
mt.ddLock.RLock()
|
||||
defer mt.ddLock.RUnlock()
|
||||
|
||||
if partitions, ok := mt.partitionName2ID[collectionID]; ok {
|
||||
if partitionID, exists := partitions[partitionName]; exists {
|
||||
return partitionID, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (mt *MetaTable) DropPartition(ctx context.Context, collectionID UniqueID, partitionID UniqueID, ts Timestamp) error {
|
||||
mt.ddLock.Lock()
|
||||
defer mt.ddLock.Unlock()
|
||||
@@ -1304,7 +1352,17 @@ func (mt *MetaTable) DropPartition(ctx context.Context, collectionID UniqueID, p
|
||||
if err := mt.catalog.AlterPartition(ctx1, coll.DBID, part, clone, metastore.MODIFY, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
mt.collID2Meta[collectionID].Partitions[idx] = clone
|
||||
// Copy-on-write so snapshots returned by ShallowClone never observe
|
||||
// in-place updates through a shared partitions backing array.
|
||||
newPartitions := make([]*model.Partition, len(coll.Partitions))
|
||||
copy(newPartitions, coll.Partitions)
|
||||
newPartitions[idx] = clone
|
||||
mt.collID2Meta[collectionID].Partitions = newPartitions
|
||||
|
||||
// Remove from partition name index when dropping
|
||||
if mt.partitionName2ID[collectionID] != nil {
|
||||
delete(mt.partitionName2ID[collectionID], part.PartitionName)
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Info("drop partition", zap.Int64("collection", collectionID),
|
||||
zap.Int64("partition", partitionID),
|
||||
@@ -1348,7 +1406,18 @@ func (mt *MetaTable) RemovePartition(ctx context.Context, collectionID UniqueID,
|
||||
if err := mt.catalog.DropPartition(ctx1, coll.DBID, collectionID, partitionID, ts); err != nil {
|
||||
return err
|
||||
}
|
||||
coll.Partitions = append(coll.Partitions[:loc], coll.Partitions[loc+1:]...)
|
||||
// COW: create new slice to avoid data race with concurrent readers
|
||||
oldPartitions := coll.Partitions
|
||||
newPartitions := make([]*model.Partition, len(oldPartitions)-1)
|
||||
copy(newPartitions[:loc], oldPartitions[:loc])
|
||||
copy(newPartitions[loc:], oldPartitions[loc+1:])
|
||||
coll.Partitions = newPartitions
|
||||
|
||||
// Remove from partition name index
|
||||
if mt.partitionName2ID[collectionID] != nil {
|
||||
delete(mt.partitionName2ID[collectionID], partition.PartitionName)
|
||||
}
|
||||
|
||||
log.Ctx(ctx).Info("remove partition", zap.Int64("collection", collectionID), zap.Int64("partition", partitionID), zap.Uint64("ts", ts))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/milvus-io/milvus-proto/go-api/v3/commonpb"
|
||||
"github.com/milvus-io/milvus-proto/go-api/v3/milvuspb"
|
||||
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
|
||||
"github.com/milvus-io/milvus/internal/metastore"
|
||||
"github.com/milvus-io/milvus/internal/metastore/kv/rootcoord"
|
||||
"github.com/milvus-io/milvus/internal/metastore/mocks"
|
||||
"github.com/milvus-io/milvus/internal/metastore/model"
|
||||
@@ -712,7 +713,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
_, err := meta.GetCollectionByName(ctx, "not_exist", "name", 101)
|
||||
_, err := meta.GetCollectionByName(ctx, "not_exist", "name", 101, false)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("get by alias", func(t *testing.T) {
|
||||
@@ -734,7 +735,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
}
|
||||
meta.aliases.insert(util.DefaultDBName, "alias", 100)
|
||||
ctx := context.Background()
|
||||
coll, err := meta.GetCollectionByName(ctx, "", "alias", 101)
|
||||
coll, err := meta.GetCollectionByName(ctx, "", "alias", 101, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(coll.Partitions))
|
||||
assert.Equal(t, UniqueID(11), coll.Partitions[0].PartitionID)
|
||||
@@ -761,7 +762,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
}
|
||||
meta.names.insert(util.DefaultDBName, "name", 100)
|
||||
ctx := context.Background()
|
||||
coll, err := meta.GetCollectionByName(ctx, "", "name", 101)
|
||||
coll, err := meta.GetCollectionByName(ctx, "", "name", 101, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(coll.Partitions))
|
||||
assert.Equal(t, UniqueID(11), coll.Partitions[0].PartitionID)
|
||||
@@ -786,7 +787,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
catalog: catalog,
|
||||
}
|
||||
ctx := context.Background()
|
||||
_, err := meta.GetCollectionByName(ctx, util.DefaultDBName, "name", 101)
|
||||
_, err := meta.GetCollectionByName(ctx, util.DefaultDBName, "name", 101, false)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
@@ -808,7 +809,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
catalog: catalog,
|
||||
}
|
||||
ctx := context.Background()
|
||||
_, err := meta.GetCollectionByName(ctx, util.DefaultDBName, "name", 101)
|
||||
_, err := meta.GetCollectionByName(ctx, util.DefaultDBName, "name", 101, false)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
})
|
||||
@@ -839,7 +840,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
catalog: catalog,
|
||||
}
|
||||
ctx := context.Background()
|
||||
coll, err := meta.GetCollectionByName(ctx, util.DefaultDBName, "name", 101)
|
||||
coll, err := meta.GetCollectionByName(ctx, util.DefaultDBName, "name", 101, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(coll.Partitions))
|
||||
assert.Equal(t, UniqueID(11), coll.Partitions[0].PartitionID)
|
||||
@@ -855,7 +856,7 @@ func TestMetaTable_GetCollectionByName(t *testing.T) {
|
||||
names: newNameDb(),
|
||||
aliases: newNameDb(),
|
||||
}
|
||||
_, err := meta.GetCollectionByName(ctx, "", "not_exist", typeutil.MaxTimestamp)
|
||||
_, err := meta.GetCollectionByName(ctx, "", "not_exist", typeutil.MaxTimestamp, false)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, merr.ErrCollectionNotFound)
|
||||
})
|
||||
@@ -1380,6 +1381,62 @@ func TestMetaTable_DropCollection_GrantCleanup(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestMetaTable_DropPartition_CopyOnWrite(t *testing.T) {
|
||||
catalog := mocks.NewRootCoordCatalog(t)
|
||||
originalPart := &model.Partition{
|
||||
PartitionID: 100,
|
||||
PartitionName: "p1",
|
||||
CollectionID: 100,
|
||||
State: pb.PartitionState_PartitionCreated,
|
||||
}
|
||||
catalog.On("AlterPartition",
|
||||
mock.Anything,
|
||||
int64(10),
|
||||
originalPart,
|
||||
mock.MatchedBy(func(newPart *model.Partition) bool {
|
||||
return newPart != nil &&
|
||||
newPart != originalPart &&
|
||||
newPart.PartitionID == originalPart.PartitionID &&
|
||||
newPart.PartitionName == originalPart.PartitionName &&
|
||||
newPart.CollectionID == originalPart.CollectionID &&
|
||||
newPart.State == pb.PartitionState_PartitionDropping
|
||||
}),
|
||||
metastore.MODIFY,
|
||||
uint64(9999),
|
||||
).Return(nil).Once()
|
||||
|
||||
meta := &MetaTable{
|
||||
catalog: catalog,
|
||||
collID2Meta: map[typeutil.UniqueID]*model.Collection{
|
||||
100: {
|
||||
CollectionID: 100,
|
||||
DBID: 10,
|
||||
State: pb.CollectionState_CollectionCreated,
|
||||
Partitions: []*model.Partition{originalPart},
|
||||
},
|
||||
},
|
||||
partitionName2ID: map[int64]map[string]int64{
|
||||
100: {"p1": 100},
|
||||
},
|
||||
}
|
||||
|
||||
snapshot, err := meta.GetCollectionByID(context.Background(), "", 100, typeutil.MaxTimestamp, true)
|
||||
require.NoError(t, err)
|
||||
require.Same(t, originalPart, snapshot.Partitions[0])
|
||||
|
||||
err = meta.DropPartition(context.Background(), 100, 100, 9999)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Same(t, originalPart, snapshot.Partitions[0])
|
||||
assert.Equal(t, pb.PartitionState_PartitionCreated, snapshot.Partitions[0].State)
|
||||
|
||||
require.Len(t, meta.collID2Meta[100].Partitions, 1)
|
||||
assert.NotSame(t, originalPart, meta.collID2Meta[100].Partitions[0])
|
||||
assert.Equal(t, pb.PartitionState_PartitionDropping, meta.collID2Meta[100].Partitions[0].State)
|
||||
assert.Equal(t, pb.PartitionState_PartitionCreated, originalPart.State)
|
||||
assert.NotContains(t, meta.partitionName2ID[100], "p1")
|
||||
}
|
||||
|
||||
func TestMetaTable_RemovePartition(t *testing.T) {
|
||||
t.Run("catalog error", func(t *testing.T) {
|
||||
catalog := mocks.NewRootCoordCatalog(t)
|
||||
@@ -1765,6 +1822,7 @@ func TestMetaTable_AddPartition(t *testing.T) {
|
||||
collID2Meta: map[typeutil.UniqueID]*model.Collection{
|
||||
100: {Name: "test", CollectionID: 100},
|
||||
},
|
||||
partitionName2ID: make(map[int64]map[string]int64),
|
||||
}
|
||||
err := meta.AddPartition(context.TODO(), &model.Partition{CollectionID: 100, State: pb.PartitionState_PartitionCreated})
|
||||
assert.NoError(t, err)
|
||||
@@ -2336,7 +2394,7 @@ func TestMetaTable_EmtpyDatabaseName(t *testing.T) {
|
||||
}
|
||||
|
||||
mt.aliases.insert(util.DefaultDBName, "aliases", 1)
|
||||
ret, err := mt.getCollectionByNameInternal(context.TODO(), "", "aliases", typeutil.MaxTimestamp)
|
||||
ret, err := mt.getCollectionByNameInternal(context.TODO(), "", "aliases", typeutil.MaxTimestamp, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), ret.CollectionID)
|
||||
})
|
||||
|
||||
@@ -67,7 +67,7 @@ type mockMetaTable struct {
|
||||
ListDatabasesFunc func(ctx context.Context, ts Timestamp) ([]*model.Database, error)
|
||||
ListCollectionsFunc func(ctx context.Context, ts Timestamp) ([]*model.Collection, error)
|
||||
AddCollectionFunc func(ctx context.Context, coll *model.Collection) error
|
||||
GetCollectionByNameFunc func(ctx context.Context, collectionName string, ts Timestamp) (*model.Collection, error)
|
||||
GetCollectionByNameFunc func(ctx context.Context, collectionName string, ts Timestamp, allowUnavailable bool) (*model.Collection, error)
|
||||
GetCollectionByIDFunc func(ctx context.Context, collectionID UniqueID, ts Timestamp, allowUnavailable bool) (*model.Collection, error)
|
||||
ChangeCollectionStateFunc func(ctx context.Context, collectionID UniqueID, state pb.CollectionState, ts Timestamp) error
|
||||
RemoveCollectionFunc func(ctx context.Context, collectionID UniqueID, ts Timestamp) error
|
||||
@@ -125,8 +125,8 @@ func (m mockMetaTable) AddCollection(ctx context.Context, coll *model.Collection
|
||||
return m.AddCollectionFunc(ctx, coll)
|
||||
}
|
||||
|
||||
func (m mockMetaTable) GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts Timestamp) (*model.Collection, error) {
|
||||
return m.GetCollectionByNameFunc(ctx, collectionName, ts)
|
||||
func (m mockMetaTable) GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts Timestamp, allowUnavailable bool) (*model.Collection, error) {
|
||||
return m.GetCollectionByNameFunc(ctx, collectionName, ts, allowUnavailable)
|
||||
}
|
||||
|
||||
func (m mockMetaTable) GetCollectionByID(ctx context.Context, dbName string, collectionID UniqueID, ts Timestamp, allowUnavailable bool) (*model.Collection, error) {
|
||||
@@ -494,7 +494,7 @@ func withInvalidMeta() Opt {
|
||||
meta.ListCollectionsFunc = func(ctx context.Context, ts Timestamp) ([]*model.Collection, error) {
|
||||
return nil, errors.New("error mock ListCollections")
|
||||
}
|
||||
meta.GetCollectionByNameFunc = func(ctx context.Context, collectionName string, ts Timestamp) (*model.Collection, error) {
|
||||
meta.GetCollectionByNameFunc = func(ctx context.Context, collectionName string, ts Timestamp, allowUnavailable bool) (*model.Collection, error) {
|
||||
return nil, errors.New("error mock GetCollectionByName")
|
||||
}
|
||||
meta.GetCollectionByIDFunc = func(ctx context.Context, collectionID typeutil.UniqueID, ts Timestamp, allowUnavailable bool) (*model.Collection, error) {
|
||||
|
||||
@@ -1966,9 +1966,9 @@ func (_c *IMetaTable_GetCollectionByIDWithMaxTs_Call) RunAndReturn(run func(cont
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetCollectionByName provides a mock function with given fields: ctx, dbName, collectionName, ts
|
||||
func (_m *IMetaTable) GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts uint64) (*model.Collection, error) {
|
||||
ret := _m.Called(ctx, dbName, collectionName, ts)
|
||||
// GetCollectionByName provides a mock function with given fields: ctx, dbName, collectionName, ts, allowUnavailable
|
||||
func (_m *IMetaTable) GetCollectionByName(ctx context.Context, dbName string, collectionName string, ts uint64, allowUnavailable bool) (*model.Collection, error) {
|
||||
ret := _m.Called(ctx, dbName, collectionName, ts, allowUnavailable)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetCollectionByName")
|
||||
@@ -1976,19 +1976,19 @@ func (_m *IMetaTable) GetCollectionByName(ctx context.Context, dbName string, co
|
||||
|
||||
var r0 *model.Collection
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, uint64) (*model.Collection, error)); ok {
|
||||
return rf(ctx, dbName, collectionName, ts)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, uint64, bool) (*model.Collection, error)); ok {
|
||||
return rf(ctx, dbName, collectionName, ts, allowUnavailable)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, uint64) *model.Collection); ok {
|
||||
r0 = rf(ctx, dbName, collectionName, ts)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, uint64, bool) *model.Collection); ok {
|
||||
r0 = rf(ctx, dbName, collectionName, ts, allowUnavailable)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.Collection)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, uint64) error); ok {
|
||||
r1 = rf(ctx, dbName, collectionName, ts)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, uint64, bool) error); ok {
|
||||
r1 = rf(ctx, dbName, collectionName, ts, allowUnavailable)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -2006,13 +2006,14 @@ type IMetaTable_GetCollectionByName_Call struct {
|
||||
// - dbName string
|
||||
// - collectionName string
|
||||
// - ts uint64
|
||||
func (_e *IMetaTable_Expecter) GetCollectionByName(ctx interface{}, dbName interface{}, collectionName interface{}, ts interface{}) *IMetaTable_GetCollectionByName_Call {
|
||||
return &IMetaTable_GetCollectionByName_Call{Call: _e.mock.On("GetCollectionByName", ctx, dbName, collectionName, ts)}
|
||||
// - allowUnavailable bool
|
||||
func (_e *IMetaTable_Expecter) GetCollectionByName(ctx interface{}, dbName interface{}, collectionName interface{}, ts interface{}, allowUnavailable interface{}) *IMetaTable_GetCollectionByName_Call {
|
||||
return &IMetaTable_GetCollectionByName_Call{Call: _e.mock.On("GetCollectionByName", ctx, dbName, collectionName, ts, allowUnavailable)}
|
||||
}
|
||||
|
||||
func (_c *IMetaTable_GetCollectionByName_Call) Run(run func(ctx context.Context, dbName string, collectionName string, ts uint64)) *IMetaTable_GetCollectionByName_Call {
|
||||
func (_c *IMetaTable_GetCollectionByName_Call) Run(run func(ctx context.Context, dbName string, collectionName string, ts uint64, allowUnavailable bool)) *IMetaTable_GetCollectionByName_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(uint64))
|
||||
run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(uint64), args[4].(bool))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
@@ -2022,7 +2023,7 @@ func (_c *IMetaTable_GetCollectionByName_Call) Return(_a0 *model.Collection, _a1
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *IMetaTable_GetCollectionByName_Call) RunAndReturn(run func(context.Context, string, string, uint64) (*model.Collection, error)) *IMetaTable_GetCollectionByName_Call {
|
||||
func (_c *IMetaTable_GetCollectionByName_Call) RunAndReturn(run func(context.Context, string, string, uint64, bool) (*model.Collection, error)) *IMetaTable_GetCollectionByName_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -2398,6 +2399,63 @@ func (_c *IMetaTable_GetPChannelInfo_Call) RunAndReturn(run func(context.Context
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetPartitionIDByName provides a mock function with given fields: collectionID, partitionName
|
||||
func (_m *IMetaTable) GetPartitionIDByName(collectionID int64, partitionName string) (int64, bool) {
|
||||
ret := _m.Called(collectionID, partitionName)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetPartitionIDByName")
|
||||
}
|
||||
|
||||
var r0 int64
|
||||
var r1 bool
|
||||
if rf, ok := ret.Get(0).(func(int64, string) (int64, bool)); ok {
|
||||
return rf(collectionID, partitionName)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(int64, string) int64); ok {
|
||||
r0 = rf(collectionID, partitionName)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(int64, string) bool); ok {
|
||||
r1 = rf(collectionID, partitionName)
|
||||
} else {
|
||||
r1 = ret.Get(1).(bool)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IMetaTable_GetPartitionIDByName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPartitionIDByName'
|
||||
type IMetaTable_GetPartitionIDByName_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetPartitionIDByName is a helper method to define mock.On call
|
||||
// - collectionID int64
|
||||
// - partitionName string
|
||||
func (_e *IMetaTable_Expecter) GetPartitionIDByName(collectionID interface{}, partitionName interface{}) *IMetaTable_GetPartitionIDByName_Call {
|
||||
return &IMetaTable_GetPartitionIDByName_Call{Call: _e.mock.On("GetPartitionIDByName", collectionID, partitionName)}
|
||||
}
|
||||
|
||||
func (_c *IMetaTable_GetPartitionIDByName_Call) Run(run func(collectionID int64, partitionName string)) *IMetaTable_GetPartitionIDByName_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(int64), args[1].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *IMetaTable_GetPartitionIDByName_Call) Return(_a0 int64, _a1 bool) *IMetaTable_GetPartitionIDByName_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *IMetaTable_GetPartitionIDByName_Call) RunAndReturn(run func(int64, string) (int64, bool)) *IMetaTable_GetPartitionIDByName_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetPrivilegeGroupRoles provides a mock function with given fields: ctx, groupName
|
||||
func (_m *IMetaTable) GetPrivilegeGroupRoles(ctx context.Context, groupName string) ([]*milvuspb.RoleEntity, error) {
|
||||
ret := _m.Called(ctx, groupName)
|
||||
|
||||
@@ -1169,7 +1169,7 @@ func (c *Core) getCollectionIDStr(ctx context.Context, db, collectionName string
|
||||
return strconv.FormatInt(collectionID, 10)
|
||||
}
|
||||
|
||||
coll, err := c.meta.GetCollectionByName(ctx, db, collectionName, typeutil.MaxTimestamp)
|
||||
coll, err := c.meta.GetCollectionByName(ctx, db, collectionName, typeutil.MaxTimestamp, false)
|
||||
if err != nil {
|
||||
return "-1"
|
||||
}
|
||||
@@ -1179,7 +1179,7 @@ func (c *Core) getCollectionIDStr(ctx context.Context, db, collectionName string
|
||||
func (c *Core) describeCollection(ctx context.Context, in *milvuspb.DescribeCollectionRequest, allowUnavailable bool) (*model.Collection, error) {
|
||||
ts := getTravelTs(in)
|
||||
if in.GetCollectionName() != "" {
|
||||
return c.meta.GetCollectionByName(ctx, in.GetDbName(), in.GetCollectionName(), ts)
|
||||
return c.meta.GetCollectionByName(ctx, in.GetDbName(), in.GetCollectionName(), ts, false)
|
||||
}
|
||||
return c.meta.GetCollectionByID(ctx, in.GetDbName(), in.GetCollectionID(), ts, allowUnavailable)
|
||||
}
|
||||
@@ -1197,10 +1197,11 @@ func convertModelToDesc(collInfo *model.Collection, aliases []string, dbName str
|
||||
resp.CollectionID = collInfo.CollectionID
|
||||
resp.VirtualChannelNames = collInfo.VirtualChannelNames
|
||||
resp.PhysicalChannelNames = collInfo.PhysicalChannelNames
|
||||
if collInfo.ShardsNum == 0 {
|
||||
collInfo.ShardsNum = int32(len(collInfo.VirtualChannelNames))
|
||||
shardsNum := collInfo.ShardsNum
|
||||
if shardsNum == 0 {
|
||||
shardsNum = int32(len(collInfo.VirtualChannelNames))
|
||||
}
|
||||
resp.ShardsNum = collInfo.ShardsNum
|
||||
resp.ShardsNum = shardsNum
|
||||
resp.ConsistencyLevel = collInfo.ConsistencyLevel
|
||||
|
||||
resp.CreatedTimestamp = collInfo.CreateTime
|
||||
@@ -1613,7 +1614,7 @@ func (c *Core) CreatePartition(ctx context.Context, in *milvuspb.CreatePartition
|
||||
zap.String("partitionName", in.GetPartitionName()))
|
||||
logger.Info("received request to create partition")
|
||||
|
||||
if err := c.broadcastCreatePartition(ctx, in); err != nil {
|
||||
if _, err := c.broadcastCreatePartition(ctx, in); err != nil {
|
||||
if errors.Is(err, errIgnoerdCreatePartition) {
|
||||
logger.Info("create partition that already exists, ignore it")
|
||||
metrics.RootCoordDDLReqCounter.WithLabelValues("CreatePartition", metrics.SuccessLabel).Inc()
|
||||
@@ -1630,6 +1631,37 @@ func (c *Core) CreatePartition(ctx context.Context, in *milvuspb.CreatePartition
|
||||
return merr.Success(), nil
|
||||
}
|
||||
|
||||
func (c *Core) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
|
||||
return &rootcoordpb.CreatePartitionResponse{Status: merr.Status(err)}, nil
|
||||
}
|
||||
|
||||
metrics.RootCoordDDLReqCounter.WithLabelValues("CreatePartition", metrics.TotalLabel).Inc()
|
||||
tr := timerecord.NewTimeRecorder("CreatePartition")
|
||||
logger := log.Ctx(ctx).With(zap.String("role", typeutil.RootCoordRole),
|
||||
zap.String("dbName", in.GetDbName()),
|
||||
zap.String("collectionName", in.GetCollectionName()),
|
||||
zap.String("partitionName", in.GetPartitionName()))
|
||||
logger.Info("received request to create partition")
|
||||
|
||||
partitionID, err := c.broadcastCreatePartition(ctx, in)
|
||||
if err != nil {
|
||||
if errors.Is(err, errIgnoerdCreatePartition) {
|
||||
logger.Info("create partition that already exists, ignore it")
|
||||
metrics.RootCoordDDLReqCounter.WithLabelValues("CreatePartition", metrics.SuccessLabel).Inc()
|
||||
return &rootcoordpb.CreatePartitionResponse{Status: merr.Success(), PartitionID: partitionID}, nil
|
||||
}
|
||||
logger.Info("failed to create partition", zap.Error(err))
|
||||
metrics.RootCoordDDLReqCounter.WithLabelValues("CreatePartition", metrics.FailLabel).Inc()
|
||||
return &rootcoordpb.CreatePartitionResponse{Status: merr.Status(err)}, nil
|
||||
}
|
||||
|
||||
metrics.RootCoordDDLReqCounter.WithLabelValues("CreatePartition", metrics.SuccessLabel).Inc()
|
||||
metrics.RootCoordDDLReqLatency.WithLabelValues("CreatePartition").Observe(float64(tr.ElapseSpan().Milliseconds()))
|
||||
logger.Info("done to create partition")
|
||||
return &rootcoordpb.CreatePartitionResponse{Status: merr.Success(), PartitionID: partitionID}, nil
|
||||
}
|
||||
|
||||
// DropPartition drop partition
|
||||
func (c *Core) DropPartition(ctx context.Context, in *milvuspb.DropPartitionRequest) (*commonpb.Status, error) {
|
||||
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
|
||||
|
||||
@@ -90,11 +90,12 @@ func initStreamingSystemAndCore(t *testing.T) *Core {
|
||||
tso.EXPECT().GenerateTSO(mock.Anything).Return(uint64(1), nil).Maybe()
|
||||
core := newTestCore(withHealthyCode(),
|
||||
withMeta(&MetaTable{
|
||||
catalog: rootcoord.NewCatalog(catalogKV),
|
||||
names: testDB,
|
||||
aliases: newNameDb(),
|
||||
dbName2Meta: make(map[string]*model.Database),
|
||||
collID2Meta: collID2Meta,
|
||||
catalog: rootcoord.NewCatalog(catalogKV),
|
||||
names: testDB,
|
||||
aliases: newNameDb(),
|
||||
dbName2Meta: make(map[string]*model.Database),
|
||||
collID2Meta: collID2Meta,
|
||||
partitionName2ID: make(map[int64]map[string]int64),
|
||||
}),
|
||||
withValidMixCoord(),
|
||||
withValidProxyManager(),
|
||||
|
||||
@@ -50,7 +50,7 @@ func (t *showPartitionTask) Execute(ctx context.Context) error {
|
||||
if t.Req.GetCollectionName() == "" {
|
||||
coll, err = t.core.meta.GetCollectionByID(ctx, t.Req.GetDbName(), t.Req.GetCollectionID(), typeutil.MaxTimestamp, t.allowUnavailable)
|
||||
} else {
|
||||
coll, err = t.core.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), typeutil.MaxTimestamp)
|
||||
coll, err = t.core.meta.GetCollectionByName(ctx, t.Req.GetDbName(), t.Req.GetCollectionName(), typeutil.MaxTimestamp, false)
|
||||
}
|
||||
if err != nil {
|
||||
t.Rsp.Status = merr.Status(err)
|
||||
|
||||
@@ -60,7 +60,7 @@ func Test_showPartitionTask_Prepare(t *testing.T) {
|
||||
func Test_showPartitionTask_Execute(t *testing.T) {
|
||||
t.Run("failed to list collections by name", func(t *testing.T) {
|
||||
metaTable := mockrootcoord.NewIMetaTable(t)
|
||||
metaTable.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, "test coll", mock.Anything).Return(nil, merr.WrapErrCollectionNotFound("test coll"))
|
||||
metaTable.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, "test coll", mock.Anything, mock.Anything).Return(nil, merr.WrapErrCollectionNotFound("test coll"))
|
||||
core := newTestCore(withMeta(metaTable))
|
||||
task := &showPartitionTask{
|
||||
baseTask: newBaseTask(context.Background(), core),
|
||||
|
||||
@@ -74,8 +74,8 @@ func TestLockerKey(t *testing.T) {
|
||||
func TestGetLockerKey(t *testing.T) {
|
||||
t.Run("describe collection task locker key", func(t *testing.T) {
|
||||
metaMock := mockrootcoord.NewIMetaTable(t)
|
||||
metaMock.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
RunAndReturn(func(ctx context.Context, s string, s2 string, u uint64) (*model.Collection, error) {
|
||||
metaMock.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
RunAndReturn(func(ctx context.Context, s string, s2 string, u uint64, allowUnavailable bool) (*model.Collection, error) {
|
||||
return nil, errors.New("not found")
|
||||
})
|
||||
c := &Core{
|
||||
@@ -128,8 +128,8 @@ func TestGetLockerKey(t *testing.T) {
|
||||
})
|
||||
t.Run("has partition task locker key", func(t *testing.T) {
|
||||
metaMock := mockrootcoord.NewIMetaTable(t)
|
||||
metaMock.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
RunAndReturn(func(ctx context.Context, s string, s2 string, u uint64) (*model.Collection, error) {
|
||||
metaMock.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
RunAndReturn(func(ctx context.Context, s string, s2 string, u uint64, allowUnavailable bool) (*model.Collection, error) {
|
||||
return &model.Collection{
|
||||
Name: "real" + s2,
|
||||
CollectionID: 111,
|
||||
@@ -165,8 +165,8 @@ func TestGetLockerKey(t *testing.T) {
|
||||
})
|
||||
t.Run("show partition task locker key", func(t *testing.T) {
|
||||
metaMock := mockrootcoord.NewIMetaTable(t)
|
||||
metaMock.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
RunAndReturn(func(ctx context.Context, s string, s2 string, u uint64) (*model.Collection, error) {
|
||||
metaMock.EXPECT().GetCollectionByName(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
RunAndReturn(func(ctx context.Context, s string, s2 string, u uint64, allowUnavailable bool) (*model.Collection, error) {
|
||||
return &model.Collection{
|
||||
Name: "real" + s2,
|
||||
CollectionID: 111,
|
||||
|
||||
@@ -170,6 +170,10 @@ func (m *GrpcRootCoordClient) CreatePartition(ctx context.Context, in *milvuspb.
|
||||
return &commonpb.Status{}, m.Err
|
||||
}
|
||||
|
||||
func (m *GrpcRootCoordClient) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*rootcoordpb.CreatePartitionResponse, error) {
|
||||
return &rootcoordpb.CreatePartitionResponse{}, m.Err
|
||||
}
|
||||
|
||||
func (m *GrpcRootCoordClient) DropPartition(ctx context.Context, in *milvuspb.DropPartitionRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||
return &commonpb.Status{}, m.Err
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ service RootCoord {
|
||||
* @return Status
|
||||
*/
|
||||
rpc CreatePartition(milvus.CreatePartitionRequest) returns (common.Status) {}
|
||||
rpc CreatePartitionV2(milvus.CreatePartitionRequest) returns (CreatePartitionResponse) {}
|
||||
|
||||
/**
|
||||
* @brief This method is used to drop partition
|
||||
@@ -315,3 +316,8 @@ message ShowCollectionIDsResponse {
|
||||
common.Status status = 1;
|
||||
repeated DBCollections db_collections = 2;
|
||||
}
|
||||
|
||||
message CreatePartitionResponse {
|
||||
common.Status status = 1;
|
||||
int64 partitionID = 2;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,7 @@ const (
|
||||
RootCoord_AlterCollectionFunction_FullMethodName = "/milvus.proto.rootcoord.RootCoord/AlterCollectionFunction"
|
||||
RootCoord_DropCollectionFunction_FullMethodName = "/milvus.proto.rootcoord.RootCoord/DropCollectionFunction"
|
||||
RootCoord_CreatePartition_FullMethodName = "/milvus.proto.rootcoord.RootCoord/CreatePartition"
|
||||
RootCoord_CreatePartitionV2_FullMethodName = "/milvus.proto.rootcoord.RootCoord/CreatePartitionV2"
|
||||
RootCoord_DropPartition_FullMethodName = "/milvus.proto.rootcoord.RootCoord/DropPartition"
|
||||
RootCoord_HasPartition_FullMethodName = "/milvus.proto.rootcoord.RootCoord/HasPartition"
|
||||
RootCoord_ShowPartitions_FullMethodName = "/milvus.proto.rootcoord.RootCoord/ShowPartitions"
|
||||
@@ -174,6 +175,7 @@ type RootCoordClient interface {
|
||||
//
|
||||
// @return Status
|
||||
CreatePartition(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*commonpb.Status, error)
|
||||
CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*CreatePartitionResponse, error)
|
||||
// *
|
||||
// @brief This method is used to drop partition
|
||||
//
|
||||
@@ -467,6 +469,15 @@ func (c *rootCoordClient) CreatePartition(ctx context.Context, in *milvuspb.Crea
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *rootCoordClient) CreatePartitionV2(ctx context.Context, in *milvuspb.CreatePartitionRequest, opts ...grpc.CallOption) (*CreatePartitionResponse, error) {
|
||||
out := new(CreatePartitionResponse)
|
||||
err := c.cc.Invoke(ctx, RootCoord_CreatePartitionV2_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *rootCoordClient) DropPartition(ctx context.Context, in *milvuspb.DropPartitionRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||
out := new(commonpb.Status)
|
||||
err := c.cc.Invoke(ctx, RootCoord_DropPartition_FullMethodName, in, out, opts...)
|
||||
@@ -968,6 +979,7 @@ type RootCoordServer interface {
|
||||
//
|
||||
// @return Status
|
||||
CreatePartition(context.Context, *milvuspb.CreatePartitionRequest) (*commonpb.Status, error)
|
||||
CreatePartitionV2(context.Context, *milvuspb.CreatePartitionRequest) (*CreatePartitionResponse, error)
|
||||
// *
|
||||
// @brief This method is used to drop partition
|
||||
//
|
||||
@@ -1113,6 +1125,9 @@ func (UnimplementedRootCoordServer) DropCollectionFunction(context.Context, *mil
|
||||
func (UnimplementedRootCoordServer) CreatePartition(context.Context, *milvuspb.CreatePartitionRequest) (*commonpb.Status, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreatePartition not implemented")
|
||||
}
|
||||
func (UnimplementedRootCoordServer) CreatePartitionV2(context.Context, *milvuspb.CreatePartitionRequest) (*CreatePartitionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreatePartitionV2 not implemented")
|
||||
}
|
||||
func (UnimplementedRootCoordServer) DropPartition(context.Context, *milvuspb.DropPartitionRequest) (*commonpb.Status, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DropPartition not implemented")
|
||||
}
|
||||
@@ -1698,6 +1713,24 @@ func _RootCoord_CreatePartition_Handler(srv interface{}, ctx context.Context, de
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RootCoord_CreatePartitionV2_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(milvuspb.CreatePartitionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RootCoordServer).CreatePartitionV2(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RootCoord_CreatePartitionV2_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RootCoordServer).CreatePartitionV2(ctx, req.(*milvuspb.CreatePartitionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RootCoord_DropPartition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(milvuspb.DropPartitionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -2647,6 +2680,10 @@ var RootCoord_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "CreatePartition",
|
||||
Handler: _RootCoord_CreatePartition_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreatePartitionV2",
|
||||
Handler: _RootCoord_CreatePartitionV2_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DropPartition",
|
||||
Handler: _RootCoord_DropPartition_Handler,
|
||||
|
||||
@@ -2052,6 +2052,8 @@ type proxyConfig struct {
|
||||
QueryNodePoolingSize ParamItem `refreshable:"false"`
|
||||
|
||||
HybridSearchRequeryPolicy ParamItem `refreshable:"true"`
|
||||
|
||||
MetaCacheGCTimeInterval ParamItem `refreshable:"true"`
|
||||
}
|
||||
|
||||
func (p *proxyConfig) init(base *BaseTable) {
|
||||
@@ -2594,6 +2596,15 @@ Disabled if the value is less or equal to 0.`,
|
||||
Export: true,
|
||||
}
|
||||
p.QueryNodePoolingSize.Init(base.mgr)
|
||||
|
||||
p.MetaCacheGCTimeInterval = ParamItem{
|
||||
Key: "proxy.metaCacheGCTimeInterval",
|
||||
Version: "2.6.13",
|
||||
Doc: "the time interval for meta cache GC, in seconds",
|
||||
DefaultValue: "300",
|
||||
Export: true,
|
||||
}
|
||||
p.MetaCacheGCTimeInterval.Init(base.mgr)
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user