mirror of
https://gitee.com/milvus-io/milvus.git
synced 2025-12-08 01:58:34 +08:00
enhance: Enable alter database props in rootcoord (#32458)
issue: #30040 --------- Signed-off-by: Wei Liu <wei.liu@zilliz.com>
This commit is contained in:
parent
c002745902
commit
04f355a802
@ -460,6 +460,10 @@ func (m *mockRootCoordClient) ListDatabases(ctx context.Context, in *milvuspb.Li
|
|||||||
panic("not implemented") // TODO: Implement
|
panic("not implemented") // TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockRootCoordClient) AlterDatabase(ctx context.Context, in *rootcoordpb.AlterDatabaseRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockRootCoordClient) AlterCollection(ctx context.Context, request *milvuspb.AlterCollectionRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
func (m *mockRootCoordClient) AlterCollection(ctx context.Context, request *milvuspb.AlterCollectionRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||||
panic("not implemented") // TODO: Implement
|
panic("not implemented") // TODO: Implement
|
||||||
}
|
}
|
||||||
|
|||||||
@ -659,3 +659,14 @@ func (c *Client) DescribeDatabase(ctx context.Context, req *rootcoordpb.Describe
|
|||||||
return client.DescribeDatabase(ctx, req)
|
return client.DescribeDatabase(ctx, req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) AlterDatabase(ctx context.Context, request *rootcoordpb.AlterDatabaseRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||||
|
request = typeutil.Clone(request)
|
||||||
|
commonpbutil.UpdateMsgBase(
|
||||||
|
request.GetBase(),
|
||||||
|
commonpbutil.FillMsgBaseFromClient(paramtable.GetNodeID(), commonpbutil.WithTargetID(c.grpcClient.GetNodeID())),
|
||||||
|
)
|
||||||
|
return wrapGrpcCall(ctx, c, func(client rootcoordpb.RootCoordClient) (*commonpb.Status, error) {
|
||||||
|
return client.AlterDatabase(ctx, request)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -236,6 +236,10 @@ func Test_NewClient(t *testing.T) {
|
|||||||
r, err := client.ListDatabases(ctx, nil)
|
r, err := client.ListDatabases(ctx, nil)
|
||||||
retCheck(retNotNil, r, err)
|
retCheck(retNotNil, r, err)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
r, err := client.AlterDatabase(ctx, nil)
|
||||||
|
retCheck(retNotNil, r, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.(*Client).grpcClient = &mock.GRPCClientBase[rootcoordpb.RootCoordClient]{
|
client.(*Client).grpcClient = &mock.GRPCClientBase[rootcoordpb.RootCoordClient]{
|
||||||
|
|||||||
@ -93,6 +93,10 @@ func (s *Server) ListDatabases(ctx context.Context, request *milvuspb.ListDataba
|
|||||||
return s.rootCoord.ListDatabases(ctx, request)
|
return s.rootCoord.ListDatabases(ctx, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) AlterDatabase(ctx context.Context, request *rootcoordpb.AlterDatabaseRequest) (*commonpb.Status, error) {
|
||||||
|
return s.rootCoord.AlterDatabase(ctx, request)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) CheckHealth(ctx context.Context, request *milvuspb.CheckHealthRequest) (*milvuspb.CheckHealthResponse, error) {
|
func (s *Server) CheckHealth(ctx context.Context, request *milvuspb.CheckHealthRequest) (*milvuspb.CheckHealthResponse, error) {
|
||||||
return s.rootCoord.CheckHealth(ctx, request)
|
return s.rootCoord.CheckHealth(ctx, request)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,11 +31,13 @@ import (
|
|||||||
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
||||||
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
|
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
|
||||||
"github.com/milvus-io/milvus/internal/mocks"
|
"github.com/milvus-io/milvus/internal/mocks"
|
||||||
|
"github.com/milvus-io/milvus/internal/proto/rootcoordpb"
|
||||||
"github.com/milvus-io/milvus/internal/rootcoord"
|
"github.com/milvus-io/milvus/internal/rootcoord"
|
||||||
"github.com/milvus-io/milvus/internal/types"
|
"github.com/milvus-io/milvus/internal/types"
|
||||||
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
|
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
|
||||||
"github.com/milvus-io/milvus/internal/util/sessionutil"
|
"github.com/milvus-io/milvus/internal/util/sessionutil"
|
||||||
"github.com/milvus-io/milvus/pkg/util/etcd"
|
"github.com/milvus-io/milvus/pkg/util/etcd"
|
||||||
|
"github.com/milvus-io/milvus/pkg/util/merr"
|
||||||
"github.com/milvus-io/milvus/pkg/util/paramtable"
|
"github.com/milvus-io/milvus/pkg/util/paramtable"
|
||||||
"github.com/milvus-io/milvus/pkg/util/tikv"
|
"github.com/milvus-io/milvus/pkg/util/tikv"
|
||||||
)
|
)
|
||||||
@ -58,6 +60,10 @@ func (m *mockCore) ListDatabases(ctx context.Context, request *milvuspb.ListData
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockCore) AlterDatabase(ctx context.Context, request *rootcoordpb.AlterDatabaseRequest) (*commonpb.Status, error) {
|
||||||
|
return &commonpb.Status{ErrorCode: commonpb.ErrorCode_Success}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockCore) RenameCollection(ctx context.Context, request *milvuspb.RenameCollectionRequest) (*commonpb.Status, error) {
|
func (m *mockCore) RenameCollection(ctx context.Context, request *milvuspb.RenameCollectionRequest) (*commonpb.Status, error) {
|
||||||
return &commonpb.Status{ErrorCode: commonpb.ErrorCode_Success}, nil
|
return &commonpb.Status{ErrorCode: commonpb.ErrorCode_Success}, nil
|
||||||
}
|
}
|
||||||
@ -197,6 +203,13 @@ func TestRun(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, commonpb.ErrorCode_Success, ret.GetStatus().GetErrorCode())
|
assert.Equal(t, commonpb.ErrorCode_Success, ret.GetStatus().GetErrorCode())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("AlterDatabase", func(t *testing.T) {
|
||||||
|
ret, err := svr.AlterDatabase(ctx, nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, merr.Ok(ret))
|
||||||
|
})
|
||||||
|
|
||||||
err = svr.Stop()
|
err = svr.Stop()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -256,6 +256,61 @@ func (_c *RootCoord_AlterCollection_Call) RunAndReturn(run func(context.Context,
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AlterDatabase provides a mock function with given fields: _a0, _a1
|
||||||
|
func (_m *RootCoord) AlterDatabase(_a0 context.Context, _a1 *rootcoordpb.AlterDatabaseRequest) (*commonpb.Status, error) {
|
||||||
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|
||||||
|
var r0 *commonpb.Status
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *rootcoordpb.AlterDatabaseRequest) (*commonpb.Status, error)); ok {
|
||||||
|
return rf(_a0, _a1)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *rootcoordpb.AlterDatabaseRequest) *commonpb.Status); ok {
|
||||||
|
r0 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*commonpb.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *rootcoordpb.AlterDatabaseRequest) error); ok {
|
||||||
|
r1 = rf(_a0, _a1)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// RootCoord_AlterDatabase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AlterDatabase'
|
||||||
|
type RootCoord_AlterDatabase_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlterDatabase is a helper method to define mock.On call
|
||||||
|
// - _a0 context.Context
|
||||||
|
// - _a1 *rootcoordpb.AlterDatabaseRequest
|
||||||
|
func (_e *RootCoord_Expecter) AlterDatabase(_a0 interface{}, _a1 interface{}) *RootCoord_AlterDatabase_Call {
|
||||||
|
return &RootCoord_AlterDatabase_Call{Call: _e.mock.On("AlterDatabase", _a0, _a1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *RootCoord_AlterDatabase_Call) Run(run func(_a0 context.Context, _a1 *rootcoordpb.AlterDatabaseRequest)) *RootCoord_AlterDatabase_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run(args[0].(context.Context), args[1].(*rootcoordpb.AlterDatabaseRequest))
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *RootCoord_AlterDatabase_Call) Return(_a0 *commonpb.Status, _a1 error) *RootCoord_AlterDatabase_Call {
|
||||||
|
_c.Call.Return(_a0, _a1)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *RootCoord_AlterDatabase_Call) RunAndReturn(run func(context.Context, *rootcoordpb.AlterDatabaseRequest) (*commonpb.Status, error)) *RootCoord_AlterDatabase_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// CheckHealth provides a mock function with given fields: _a0, _a1
|
// CheckHealth provides a mock function with given fields: _a0, _a1
|
||||||
func (_m *RootCoord) CheckHealth(_a0 context.Context, _a1 *milvuspb.CheckHealthRequest) (*milvuspb.CheckHealthResponse, error) {
|
func (_m *RootCoord) CheckHealth(_a0 context.Context, _a1 *milvuspb.CheckHealthRequest) (*milvuspb.CheckHealthResponse, error) {
|
||||||
ret := _m.Called(_a0, _a1)
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|||||||
@ -313,6 +313,76 @@ func (_c *MockRootCoordClient_AlterCollection_Call) RunAndReturn(run func(contex
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AlterDatabase provides a mock function with given fields: ctx, in, opts
|
||||||
|
func (_m *MockRootCoordClient) AlterDatabase(ctx context.Context, in *rootcoordpb.AlterDatabaseRequest, opts ...grpc.CallOption) (*commonpb.Status, 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...)
|
||||||
|
|
||||||
|
var r0 *commonpb.Status
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *rootcoordpb.AlterDatabaseRequest, ...grpc.CallOption) (*commonpb.Status, error)); ok {
|
||||||
|
return rf(ctx, in, opts...)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *rootcoordpb.AlterDatabaseRequest, ...grpc.CallOption) *commonpb.Status); ok {
|
||||||
|
r0 = rf(ctx, in, opts...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*commonpb.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *rootcoordpb.AlterDatabaseRequest, ...grpc.CallOption) error); ok {
|
||||||
|
r1 = rf(ctx, in, opts...)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockRootCoordClient_AlterDatabase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AlterDatabase'
|
||||||
|
type MockRootCoordClient_AlterDatabase_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlterDatabase is a helper method to define mock.On call
|
||||||
|
// - ctx context.Context
|
||||||
|
// - in *rootcoordpb.AlterDatabaseRequest
|
||||||
|
// - opts ...grpc.CallOption
|
||||||
|
func (_e *MockRootCoordClient_Expecter) AlterDatabase(ctx interface{}, in interface{}, opts ...interface{}) *MockRootCoordClient_AlterDatabase_Call {
|
||||||
|
return &MockRootCoordClient_AlterDatabase_Call{Call: _e.mock.On("AlterDatabase",
|
||||||
|
append([]interface{}{ctx, in}, opts...)...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRootCoordClient_AlterDatabase_Call) Run(run func(ctx context.Context, in *rootcoordpb.AlterDatabaseRequest, opts ...grpc.CallOption)) *MockRootCoordClient_AlterDatabase_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].(*rootcoordpb.AlterDatabaseRequest), variadicArgs...)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRootCoordClient_AlterDatabase_Call) Return(_a0 *commonpb.Status, _a1 error) *MockRootCoordClient_AlterDatabase_Call {
|
||||||
|
_c.Call.Return(_a0, _a1)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockRootCoordClient_AlterDatabase_Call) RunAndReturn(run func(context.Context, *rootcoordpb.AlterDatabaseRequest, ...grpc.CallOption) (*commonpb.Status, error)) *MockRootCoordClient_AlterDatabase_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// CheckHealth provides a mock function with given fields: ctx, in, opts
|
// CheckHealth provides a mock function with given fields: ctx, in, opts
|
||||||
func (_m *MockRootCoordClient) CheckHealth(ctx context.Context, in *milvuspb.CheckHealthRequest, opts ...grpc.CallOption) (*milvuspb.CheckHealthResponse, error) {
|
func (_m *MockRootCoordClient) CheckHealth(ctx context.Context, in *milvuspb.CheckHealthRequest, opts ...grpc.CallOption) (*milvuspb.CheckHealthResponse, error) {
|
||||||
_va := make([]interface{}, len(opts))
|
_va := make([]interface{}, len(opts))
|
||||||
|
|||||||
@ -141,6 +141,7 @@ service RootCoord {
|
|||||||
rpc DropDatabase(milvus.DropDatabaseRequest) returns (common.Status) {}
|
rpc DropDatabase(milvus.DropDatabaseRequest) returns (common.Status) {}
|
||||||
rpc ListDatabases(milvus.ListDatabasesRequest) returns (milvus.ListDatabasesResponse) {}
|
rpc ListDatabases(milvus.ListDatabasesRequest) returns (milvus.ListDatabasesResponse) {}
|
||||||
rpc DescribeDatabase(DescribeDatabaseRequest) returns(DescribeDatabaseResponse){}
|
rpc DescribeDatabase(DescribeDatabaseRequest) returns(DescribeDatabaseResponse){}
|
||||||
|
rpc AlterDatabase(AlterDatabaseRequest) returns(common.Status){}
|
||||||
}
|
}
|
||||||
|
|
||||||
message AllocTimestampRequest {
|
message AllocTimestampRequest {
|
||||||
@ -218,3 +219,10 @@ message DescribeDatabaseResponse {
|
|||||||
int64 dbID = 3;
|
int64 dbID = 3;
|
||||||
uint64 created_timestamp = 4;
|
uint64 created_timestamp = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message AlterDatabaseRequest {
|
||||||
|
common.MsgBase base = 1;
|
||||||
|
string db_name = 2;
|
||||||
|
string db_id = 3;
|
||||||
|
repeated common.KeyValuePair properties = 4;
|
||||||
|
}
|
||||||
|
|||||||
@ -1115,6 +1115,10 @@ func (coord *RootCoordMock) DescribeDatabase(ctx context.Context, in *rootcoordp
|
|||||||
return &rootcoordpb.DescribeDatabaseResponse{}, nil
|
return &rootcoordpb.DescribeDatabaseResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (coord *RootCoordMock) AlterDatabase(ctx context.Context, in *rootcoordpb.AlterDatabaseRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||||
|
return &commonpb.Status{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type DescribeCollectionFunc func(ctx context.Context, request *milvuspb.DescribeCollectionRequest, opts ...grpc.CallOption) (*milvuspb.DescribeCollectionResponse, error)
|
type DescribeCollectionFunc func(ctx context.Context, request *milvuspb.DescribeCollectionRequest, opts ...grpc.CallOption) (*milvuspb.DescribeCollectionResponse, error)
|
||||||
|
|
||||||
type ShowPartitionsFunc func(ctx context.Context, request *milvuspb.ShowPartitionsRequest, opts ...grpc.CallOption) (*milvuspb.ShowPartitionsResponse, error)
|
type ShowPartitionsFunc func(ctx context.Context, request *milvuspb.ShowPartitionsRequest, opts ...grpc.CallOption) (*milvuspb.ShowPartitionsResponse, error)
|
||||||
|
|||||||
93
internal/rootcoord/alter_database_task.go
Normal file
93
internal/rootcoord/alter_database_task.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// 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 rootcoord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
||||||
|
"github.com/milvus-io/milvus/internal/proto/rootcoordpb"
|
||||||
|
"github.com/milvus-io/milvus/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type alterDatabaseTask struct {
|
||||||
|
baseTask
|
||||||
|
Req *rootcoordpb.AlterDatabaseRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *alterDatabaseTask) Prepare(ctx context.Context) error {
|
||||||
|
if a.Req.GetDbName() == "" {
|
||||||
|
return fmt.Errorf("alter database failed, database name does not exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *alterDatabaseTask) Execute(ctx context.Context) error {
|
||||||
|
// Now we only support alter properties of database
|
||||||
|
if a.Req.GetProperties() == nil {
|
||||||
|
return errors.New("only support alter database properties, but database properties is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
oldDB, err := a.core.meta.GetDatabaseByName(ctx, a.Req.GetDbName(), a.ts)
|
||||||
|
if err != nil {
|
||||||
|
log.Ctx(ctx).Warn("get database failed during changing database props",
|
||||||
|
zap.String("databaseName", a.Req.GetDbName()), zap.Uint64("ts", a.ts))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDB := oldDB.Clone()
|
||||||
|
ret := updateProperties(oldDB.Properties, a.Req.GetProperties())
|
||||||
|
newDB.Properties = ret
|
||||||
|
|
||||||
|
ts := a.GetTs()
|
||||||
|
redoTask := newBaseRedoTask(a.core.stepExecutor)
|
||||||
|
redoTask.AddSyncStep(&AlterDatabaseStep{
|
||||||
|
baseStep: baseStep{core: a.core},
|
||||||
|
oldDB: oldDB,
|
||||||
|
newDB: newDB,
|
||||||
|
ts: ts,
|
||||||
|
})
|
||||||
|
|
||||||
|
return redoTask.Execute(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateProperties(oldProps []*commonpb.KeyValuePair, updatedProps []*commonpb.KeyValuePair) []*commonpb.KeyValuePair {
|
||||||
|
props := make(map[string]string)
|
||||||
|
for _, prop := range oldProps {
|
||||||
|
props[prop.Key] = prop.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prop := range updatedProps {
|
||||||
|
props[prop.Key] = prop.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
propKV := make([]*commonpb.KeyValuePair, 0)
|
||||||
|
|
||||||
|
for key, value := range props {
|
||||||
|
propKV = append(propKV, &commonpb.KeyValuePair{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return propKV
|
||||||
|
}
|
||||||
180
internal/rootcoord/alter_database_task_test.go
Normal file
180
internal/rootcoord/alter_database_task_test.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// 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 rootcoord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
||||||
|
"github.com/milvus-io/milvus/internal/metastore/model"
|
||||||
|
"github.com/milvus-io/milvus/internal/proto/rootcoordpb"
|
||||||
|
mockrootcoord "github.com/milvus-io/milvus/internal/rootcoord/mocks"
|
||||||
|
"github.com/milvus-io/milvus/pkg/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_alterDatabaseTask_Prepare(t *testing.T) {
|
||||||
|
t.Run("invalid collectionID", func(t *testing.T) {
|
||||||
|
task := &alterDatabaseTask{Req: &rootcoordpb.AlterDatabaseRequest{}}
|
||||||
|
err := task.Prepare(context.Background())
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("normal case", func(t *testing.T) {
|
||||||
|
task := &alterDatabaseTask{
|
||||||
|
Req: &rootcoordpb.AlterDatabaseRequest{
|
||||||
|
DbName: "cn",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := task.Prepare(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_alterDatabaseTask_Execute(t *testing.T) {
|
||||||
|
properties := []*commonpb.KeyValuePair{
|
||||||
|
{
|
||||||
|
Key: common.CollectionTTLConfigKey,
|
||||||
|
Value: "3600",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("properties is empty", func(t *testing.T) {
|
||||||
|
task := &alterDatabaseTask{Req: &rootcoordpb.AlterDatabaseRequest{}}
|
||||||
|
err := task.Execute(context.Background())
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("failed to create alias", func(t *testing.T) {
|
||||||
|
core := newTestCore(withInvalidMeta())
|
||||||
|
task := &alterDatabaseTask{
|
||||||
|
baseTask: newBaseTask(context.Background(), core),
|
||||||
|
Req: &rootcoordpb.AlterDatabaseRequest{
|
||||||
|
DbName: "cn",
|
||||||
|
Properties: properties,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := task.Execute(context.Background())
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("alter step failed", func(t *testing.T) {
|
||||||
|
meta := mockrootcoord.NewIMetaTable(t)
|
||||||
|
meta.On("GetDatabaseByName",
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
).Return(&model.Database{ID: int64(1)}, nil)
|
||||||
|
meta.On("AlterDatabase",
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
).Return(errors.New("err"))
|
||||||
|
|
||||||
|
core := newTestCore(withMeta(meta))
|
||||||
|
task := &alterDatabaseTask{
|
||||||
|
baseTask: newBaseTask(context.Background(), core),
|
||||||
|
Req: &rootcoordpb.AlterDatabaseRequest{
|
||||||
|
DbName: "cn",
|
||||||
|
Properties: properties,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := task.Execute(context.Background())
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("alter successfully", func(t *testing.T) {
|
||||||
|
meta := mockrootcoord.NewIMetaTable(t)
|
||||||
|
meta.On("GetDatabaseByName",
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
).Return(&model.Database{ID: int64(1)}, nil)
|
||||||
|
meta.On("AlterDatabase",
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
mock.Anything,
|
||||||
|
).Return(nil)
|
||||||
|
|
||||||
|
core := newTestCore(withMeta(meta))
|
||||||
|
task := &alterDatabaseTask{
|
||||||
|
baseTask: newBaseTask(context.Background(), core),
|
||||||
|
Req: &rootcoordpb.AlterDatabaseRequest{
|
||||||
|
DbName: "cn",
|
||||||
|
Properties: properties,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := task.Execute(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("test update collection props", func(t *testing.T) {
|
||||||
|
oldProps := []*commonpb.KeyValuePair{
|
||||||
|
{
|
||||||
|
Key: common.CollectionTTLConfigKey,
|
||||||
|
Value: "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProps1 := []*commonpb.KeyValuePair{
|
||||||
|
{
|
||||||
|
Key: common.CollectionAutoCompactionKey,
|
||||||
|
Value: "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := updateProperties(oldProps, updateProps1)
|
||||||
|
|
||||||
|
assert.Contains(t, ret, &commonpb.KeyValuePair{
|
||||||
|
Key: common.CollectionTTLConfigKey,
|
||||||
|
Value: "1",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Contains(t, ret, &commonpb.KeyValuePair{
|
||||||
|
Key: common.CollectionAutoCompactionKey,
|
||||||
|
Value: "true",
|
||||||
|
})
|
||||||
|
|
||||||
|
updateProps2 := []*commonpb.KeyValuePair{
|
||||||
|
{
|
||||||
|
Key: common.CollectionTTLConfigKey,
|
||||||
|
Value: "2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret2 := updateProperties(ret, updateProps2)
|
||||||
|
|
||||||
|
assert.Contains(t, ret2, &commonpb.KeyValuePair{
|
||||||
|
Key: common.CollectionTTLConfigKey,
|
||||||
|
Value: "2",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Contains(t, ret2, &commonpb.KeyValuePair{
|
||||||
|
Key: common.CollectionAutoCompactionKey,
|
||||||
|
Value: "true",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -49,6 +49,7 @@ type IMetaTable interface {
|
|||||||
CreateDatabase(ctx context.Context, db *model.Database, ts typeutil.Timestamp) error
|
CreateDatabase(ctx context.Context, db *model.Database, ts typeutil.Timestamp) error
|
||||||
DropDatabase(ctx context.Context, dbName string, ts typeutil.Timestamp) error
|
DropDatabase(ctx context.Context, dbName string, ts typeutil.Timestamp) error
|
||||||
ListDatabases(ctx context.Context, ts typeutil.Timestamp) ([]*model.Database, error)
|
ListDatabases(ctx context.Context, ts typeutil.Timestamp) ([]*model.Database, error)
|
||||||
|
AlterDatabase(ctx context.Context, oldDB *model.Database, newDB *model.Database, ts typeutil.Timestamp) error
|
||||||
|
|
||||||
AddCollection(ctx context.Context, coll *model.Collection) error
|
AddCollection(ctx context.Context, coll *model.Collection) error
|
||||||
ChangeCollectionState(ctx context.Context, collectionID UniqueID, state pb.CollectionState, ts Timestamp) error
|
ChangeCollectionState(ctx context.Context, collectionID UniqueID, state pb.CollectionState, ts Timestamp) error
|
||||||
|
|||||||
@ -289,6 +289,51 @@ func (_c *IMetaTable_AlterCredential_Call) RunAndReturn(run func(*internalpb.Cre
|
|||||||
return _c
|
return _c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AlterDatabase provides a mock function with given fields: ctx, oldDB, newDB, ts
|
||||||
|
func (_m *IMetaTable) AlterDatabase(ctx context.Context, oldDB *model.Database, newDB *model.Database, ts uint64) error {
|
||||||
|
ret := _m.Called(ctx, oldDB, newDB, ts)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *model.Database, *model.Database, uint64) error); ok {
|
||||||
|
r0 = rf(ctx, oldDB, newDB, ts)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMetaTable_AlterDatabase_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AlterDatabase'
|
||||||
|
type IMetaTable_AlterDatabase_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// AlterDatabase is a helper method to define mock.On call
|
||||||
|
// - ctx context.Context
|
||||||
|
// - oldDB *model.Database
|
||||||
|
// - newDB *model.Database
|
||||||
|
// - ts uint64
|
||||||
|
func (_e *IMetaTable_Expecter) AlterDatabase(ctx interface{}, oldDB interface{}, newDB interface{}, ts interface{}) *IMetaTable_AlterDatabase_Call {
|
||||||
|
return &IMetaTable_AlterDatabase_Call{Call: _e.mock.On("AlterDatabase", ctx, oldDB, newDB, ts)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *IMetaTable_AlterDatabase_Call) Run(run func(ctx context.Context, oldDB *model.Database, newDB *model.Database, ts uint64)) *IMetaTable_AlterDatabase_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run(args[0].(context.Context), args[1].(*model.Database), args[2].(*model.Database), args[3].(uint64))
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *IMetaTable_AlterDatabase_Call) Return(_a0 error) *IMetaTable_AlterDatabase_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *IMetaTable_AlterDatabase_Call) RunAndReturn(run func(context.Context, *model.Database, *model.Database, uint64) error) *IMetaTable_AlterDatabase_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
// ChangeCollectionState provides a mock function with given fields: ctx, collectionID, state, ts
|
// ChangeCollectionState provides a mock function with given fields: ctx, collectionID, state, ts
|
||||||
func (_m *IMetaTable) ChangeCollectionState(ctx context.Context, collectionID int64, state etcdpb.CollectionState, ts uint64) error {
|
func (_m *IMetaTable) ChangeCollectionState(ctx context.Context, collectionID int64, state etcdpb.CollectionState, ts uint64) error {
|
||||||
ret := _m.Called(ctx, collectionID, state, ts)
|
ret := _m.Called(ctx, collectionID, state, ts)
|
||||||
|
|||||||
@ -1290,6 +1290,58 @@ func (c *Core) AlterCollection(ctx context.Context, in *milvuspb.AlterCollection
|
|||||||
return merr.Success(), nil
|
return merr.Success(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Core) AlterDatabase(ctx context.Context, in *rootcoordpb.AlterDatabaseRequest) (*commonpb.Status, error) {
|
||||||
|
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
|
||||||
|
return merr.Status(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
method := "AlterDatabase"
|
||||||
|
|
||||||
|
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.TotalLabel).Inc()
|
||||||
|
tr := timerecord.NewTimeRecorder(method)
|
||||||
|
|
||||||
|
log.Ctx(ctx).Info("received request to alter database",
|
||||||
|
zap.String("role", typeutil.RootCoordRole),
|
||||||
|
zap.String("name", in.GetDbName()),
|
||||||
|
zap.Any("props", in.Properties))
|
||||||
|
|
||||||
|
t := &alterDatabaseTask{
|
||||||
|
baseTask: newBaseTask(ctx, c),
|
||||||
|
Req: in,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.scheduler.AddTask(t); err != nil {
|
||||||
|
log.Warn("failed to enqueue request to alter database",
|
||||||
|
zap.String("role", typeutil.RootCoordRole),
|
||||||
|
zap.String("name", in.GetDbName()),
|
||||||
|
zap.Error(err))
|
||||||
|
|
||||||
|
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
|
||||||
|
return merr.Status(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.WaitToFinish(); err != nil {
|
||||||
|
log.Warn("failed to alter database",
|
||||||
|
zap.String("role", typeutil.RootCoordRole),
|
||||||
|
zap.Error(err),
|
||||||
|
zap.String("name", in.GetDbName()),
|
||||||
|
zap.Uint64("ts", t.GetTs()))
|
||||||
|
|
||||||
|
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
|
||||||
|
return merr.Status(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
|
||||||
|
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
|
||||||
|
metrics.RootCoordDDLReqLatencyInQueue.WithLabelValues(method).Observe(float64(t.queueDur.Milliseconds()))
|
||||||
|
|
||||||
|
log.Ctx(ctx).Info("done to alter database",
|
||||||
|
zap.String("role", typeutil.RootCoordRole),
|
||||||
|
zap.String("name", in.GetDbName()),
|
||||||
|
zap.Uint64("ts", t.GetTs()))
|
||||||
|
return merr.Success(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreatePartition create partition
|
// CreatePartition create partition
|
||||||
func (c *Core) CreatePartition(ctx context.Context, in *milvuspb.CreatePartitionRequest) (*commonpb.Status, error) {
|
func (c *Core) CreatePartition(ctx context.Context, in *milvuspb.CreatePartitionRequest) (*commonpb.Status, error) {
|
||||||
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
|
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
|
||||||
|
|||||||
@ -179,6 +179,45 @@ func TestRootCoord_ListDatabases(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRootCoord_AlterDatabase(t *testing.T) {
|
||||||
|
t.Run("not healthy", func(t *testing.T) {
|
||||||
|
c := newTestCore(withAbnormalCode())
|
||||||
|
ctx := context.Background()
|
||||||
|
resp, err := c.AlterDatabase(ctx, &rootcoordpb.AlterDatabaseRequest{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, commonpb.ErrorCode_NotReadyServe, resp.GetErrorCode())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("failed to add task", func(t *testing.T) {
|
||||||
|
c := newTestCore(withHealthyCode(),
|
||||||
|
withInvalidScheduler())
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
resp, err := c.AlterDatabase(ctx, &rootcoordpb.AlterDatabaseRequest{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetErrorCode())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("failed to execute", func(t *testing.T) {
|
||||||
|
c := newTestCore(withHealthyCode(),
|
||||||
|
withTaskFailScheduler())
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
resp, err := c.AlterDatabase(ctx, &rootcoordpb.AlterDatabaseRequest{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetErrorCode())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ok", func(t *testing.T) {
|
||||||
|
c := newTestCore(withHealthyCode(),
|
||||||
|
withValidScheduler())
|
||||||
|
ctx := context.Background()
|
||||||
|
resp, err := c.AlterDatabase(ctx, &rootcoordpb.AlterDatabaseRequest{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, commonpb.ErrorCode_Success, resp.GetErrorCode())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestRootCoord_CreateCollection(t *testing.T) {
|
func TestRootCoord_CreateCollection(t *testing.T) {
|
||||||
t.Run("not healthy", func(t *testing.T) {
|
t.Run("not healthy", func(t *testing.T) {
|
||||||
c := newTestCore(withAbnormalCode())
|
c := newTestCore(withAbnormalCode())
|
||||||
|
|||||||
@ -459,6 +459,22 @@ func (b *BroadcastAlteredCollectionStep) Desc() string {
|
|||||||
return fmt.Sprintf("broadcast altered collection, collectionID: %d", b.req.CollectionID)
|
return fmt.Sprintf("broadcast altered collection, collectionID: %d", b.req.CollectionID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AlterDatabaseStep struct {
|
||||||
|
baseStep
|
||||||
|
oldDB *model.Database
|
||||||
|
newDB *model.Database
|
||||||
|
ts Timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AlterDatabaseStep) Execute(ctx context.Context) ([]nestedStep, error) {
|
||||||
|
err := a.core.meta.AlterDatabase(ctx, a.oldDB, a.newDB, a.ts)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AlterDatabaseStep) Desc() string {
|
||||||
|
return fmt.Sprintf("alter database, databaseID: %d, databaseName: %s, ts: %d", a.oldDB.ID, a.oldDB.Name, a.ts)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
confirmGCInterval = time.Minute * 20
|
confirmGCInterval = time.Minute * 20
|
||||||
allPartition UniqueID = -1
|
allPartition UniqueID = -1
|
||||||
|
|||||||
@ -258,6 +258,10 @@ func (m *GrpcRootCoordClient) AlterCollection(ctx context.Context, in *milvuspb.
|
|||||||
return &commonpb.Status{}, m.Err
|
return &commonpb.Status{}, m.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *GrpcRootCoordClient) AlterDatabase(ctx context.Context, in *rootcoordpb.AlterDatabaseRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
|
||||||
|
return &commonpb.Status{}, m.Err
|
||||||
|
}
|
||||||
|
|
||||||
func (m *GrpcRootCoordClient) Close() error {
|
func (m *GrpcRootCoordClient) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user