Support rename db name of the collection (#26543)

Signed-off-by: jaime <yun.zhang@zilliz.com>
This commit is contained in:
jaime 2023-08-29 14:54:26 +08:00 committed by GitHub
parent c603f1c244
commit 9f61dba3f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 24 deletions

View File

@ -452,12 +452,18 @@ func (kc *Catalog) alterModifyCollection(oldColl *model.Collection, newColl *mod
oldCollClone.CreateTime = newColl.CreateTime oldCollClone.CreateTime = newColl.CreateTime
oldCollClone.ConsistencyLevel = newColl.ConsistencyLevel oldCollClone.ConsistencyLevel = newColl.ConsistencyLevel
oldCollClone.State = newColl.State oldCollClone.State = newColl.State
key := BuildCollectionKey(newColl.DBID, oldColl.CollectionID)
oldKey := BuildCollectionKey(oldColl.DBID, oldColl.CollectionID)
newKey := BuildCollectionKey(newColl.DBID, oldColl.CollectionID)
value, err := proto.Marshal(model.MarshalCollectionModel(oldCollClone)) value, err := proto.Marshal(model.MarshalCollectionModel(oldCollClone))
if err != nil { if err != nil {
return err return err
} }
return kc.Snapshot.Save(key, string(value), ts) saves := map[string]string{newKey: string(value)}
if oldKey == newKey {
return kc.Snapshot.Save(newKey, string(value), ts)
}
return kc.Snapshot.MultiSaveAndRemoveWithPrefix(saves, []string{oldKey}, ts)
} }
func (kc *Catalog) AlterCollection(ctx context.Context, oldColl *model.Collection, newColl *model.Collection, alterType metastore.AlterType, ts typeutil.Timestamp) error { func (kc *Catalog) AlterCollection(ctx context.Context, oldColl *model.Collection, newColl *model.Collection, alterType metastore.AlterType, ts typeutil.Timestamp) error {

View File

@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/atomic" "go.uber.org/atomic"
"golang.org/x/exp/maps"
) )
var ( var (
@ -1007,6 +1008,24 @@ func TestCatalog_AlterCollection(t *testing.T) {
err := kc.AlterCollection(ctx, oldC, newC, metastore.MODIFY, 0) err := kc.AlterCollection(ctx, oldC, newC, metastore.MODIFY, 0)
assert.Error(t, err) assert.Error(t, err)
}) })
t.Run("modify db name", func(t *testing.T) {
var collectionID int64 = 1
snapshot := kv.NewMockSnapshotKV()
snapshot.MultiSaveAndRemoveWithPrefixFunc = func(saves map[string]string, removals []string, ts typeutil.Timestamp) error {
assert.ElementsMatch(t, []string{BuildCollectionKey(0, collectionID)}, removals)
assert.Equal(t, len(saves), 1)
assert.Contains(t, maps.Keys(saves), BuildCollectionKey(1, collectionID))
return nil
}
kc := &Catalog{Snapshot: snapshot}
ctx := context.Background()
oldC := &model.Collection{DBID: 0, CollectionID: collectionID, State: pb.CollectionState_CollectionCreated}
newC := &model.Collection{DBID: 1, CollectionID: collectionID, State: pb.CollectionState_CollectionCreated}
err := kc.AlterCollection(ctx, oldC, newC, metastore.MODIFY, 0)
assert.NoError(t, err)
})
} }
func TestCatalog_AlterPartition(t *testing.T) { func TestCatalog_AlterPartition(t *testing.T) {

View File

@ -42,9 +42,12 @@ func (t *listDatabaseTask) Execute(ctx context.Context) error {
} }
dbNames := make([]string, 0, len(ret)) dbNames := make([]string, 0, len(ret))
createdTimes := make([]uint64, 0, len(ret))
for _, db := range ret { for _, db := range ret {
dbNames = append(dbNames, db.Name) dbNames = append(dbNames, db.Name)
createdTimes = append(createdTimes, db.CreatedTime)
} }
t.Resp.DbNames = dbNames t.Resp.DbNames = dbNames
t.Resp.CreatedTimestamp = createdTimes
return nil return nil
} }

View File

@ -65,7 +65,7 @@ type IMetaTable interface {
DropAlias(ctx context.Context, dbName string, alias string, ts Timestamp) error DropAlias(ctx context.Context, dbName string, alias string, ts Timestamp) error
AlterAlias(ctx context.Context, dbName string, alias string, collectionName string, ts Timestamp) error AlterAlias(ctx context.Context, dbName string, alias string, collectionName string, ts Timestamp) error
AlterCollection(ctx context.Context, oldColl *model.Collection, newColl *model.Collection, ts Timestamp) error AlterCollection(ctx context.Context, oldColl *model.Collection, newColl *model.Collection, ts Timestamp) error
RenameCollection(ctx context.Context, dbName string, oldName string, newName string, ts Timestamp) error RenameCollection(ctx context.Context, dbName string, oldName string, newDBName string, newName string, ts Timestamp) error
// TODO: it'll be a big cost if we handle the time travel logic, since we should always list all aliases in catalog. // TODO: it'll be a big cost if we handle the time travel logic, since we should always list all aliases in catalog.
IsAlias(db, name string) bool IsAlias(db, name string) bool
@ -700,14 +700,15 @@ func (mt *MetaTable) AlterCollection(ctx context.Context, oldColl *model.Collect
return nil return nil
} }
func (mt *MetaTable) RenameCollection(ctx context.Context, dbName string, oldName string, newName string, ts Timestamp) error { func (mt *MetaTable) RenameCollection(ctx context.Context, dbName string, oldName string, newDBName string, newName string, ts Timestamp) error {
mt.ddLock.Lock() mt.ddLock.Lock()
defer mt.ddLock.Unlock() defer mt.ddLock.Unlock()
ctx = contextutil.WithTenantID(ctx, Params.CommonCfg.ClusterName.GetValue()) ctx = contextutil.WithTenantID(ctx, Params.CommonCfg.ClusterName.GetValue())
log := log.Ctx(ctx).With( log := log.Ctx(ctx).With(
zap.String("db", dbName), zap.String("oldDBName", dbName),
zap.String("oldName", oldName), zap.String("oldName", oldName),
zap.String("newDBName", newDBName),
zap.String("newName", newName), zap.String("newName", newName),
) )
@ -717,17 +718,29 @@ func (mt *MetaTable) RenameCollection(ctx context.Context, dbName string, oldNam
dbName = util.DefaultDBName dbName = util.DefaultDBName
} }
if newDBName == "" {
log.Warn("target db name is empty")
newDBName = dbName
}
// check target db
targetDB, ok := mt.dbName2Meta[newDBName]
if !ok {
return fmt.Errorf("target database:%s not found", newDBName)
}
//old collection should not be an alias //old collection should not be an alias
_, ok := mt.aliases.get(dbName, oldName) _, ok = mt.aliases.get(dbName, oldName)
if ok { if ok {
log.Warn("unsupported use a alias to rename collection") log.Warn("unsupported use a alias to rename collection")
return fmt.Errorf("unsupported use an alias to rename collection, alias:%s", oldName) return fmt.Errorf("unsupported use an alias to rename collection, alias:%s", oldName)
} }
// check new collection already exists // check new collection already exists
newColl, err := mt.getCollectionByNameInternal(ctx, dbName, newName, ts) newColl, err := mt.getCollectionByNameInternal(ctx, newDBName, newName, ts)
if newColl != nil { if newColl != nil {
return fmt.Errorf("duplicated new collection name :%s with other collection name or alias", newName) log.Warn("check new collection fail")
return fmt.Errorf("duplicated new collection name %s:%s with other collection name or alias", newDBName, newName)
} }
if err != nil && !common.IsCollectionNotExistErrorV2(err) { if err != nil && !common.IsCollectionNotExistErrorV2(err) {
log.Warn("check new collection name fail") log.Warn("check new collection name fail")
@ -737,16 +750,24 @@ func (mt *MetaTable) RenameCollection(ctx context.Context, dbName string, oldNam
// get old collection meta // get old collection meta
oldColl, err := mt.getCollectionByNameInternal(ctx, dbName, oldName, ts) oldColl, err := mt.getCollectionByNameInternal(ctx, dbName, oldName, ts)
if err != nil { if err != nil {
log.Warn("get old collection fail")
return err return err
} }
// unsupported rename collection while the collection has aliases
aliases := mt.listAliasesByID(oldColl.CollectionID)
if len(aliases) > 0 && oldColl.DBID != targetDB.ID {
return fmt.Errorf("fail to rename db name, must drop all aliases of this collection before rename")
}
newColl = oldColl.Clone() newColl = oldColl.Clone()
newColl.Name = newName newColl.Name = newName
newColl.DBID = targetDB.ID
if err := mt.catalog.AlterCollection(ctx, oldColl, newColl, metastore.MODIFY, ts); err != nil { if err := mt.catalog.AlterCollection(ctx, oldColl, newColl, metastore.MODIFY, ts); err != nil {
return err return err
} }
mt.names.insert(dbName, newName, oldColl.CollectionID) mt.names.insert(newDBName, newName, oldColl.CollectionID)
mt.names.remove(dbName, oldName) mt.names.remove(dbName, oldName)
mt.collID2Meta[oldColl.CollectionID] = newColl mt.collID2Meta[oldColl.CollectionID] = newColl

View File

@ -1170,7 +1170,16 @@ func TestMetaTable_RenameCollection(t *testing.T) {
aliases: newNameDb(), aliases: newNameDb(),
} }
meta.names.insert("", "alias", 1) meta.names.insert("", "alias", 1)
err := meta.RenameCollection(context.TODO(), "", "alias", "new", typeutil.MaxTimestamp) err := meta.RenameCollection(context.TODO(), "", "alias", "", "new", typeutil.MaxTimestamp)
assert.Error(t, err)
})
t.Run("target db doesn't exist", func(t *testing.T) {
meta := &MetaTable{
names: newNameDb(),
aliases: newNameDb(),
}
err := meta.RenameCollection(context.TODO(), "", "non-exists", "non-exists", "new", typeutil.MaxTimestamp)
assert.Error(t, err) assert.Error(t, err)
}) })
@ -1179,7 +1188,7 @@ func TestMetaTable_RenameCollection(t *testing.T) {
names: newNameDb(), names: newNameDb(),
aliases: newNameDb(), aliases: newNameDb(),
} }
err := meta.RenameCollection(context.TODO(), "", "non-exists", "new", typeutil.MaxTimestamp) err := meta.RenameCollection(context.TODO(), "", "non-exists", "", "new", typeutil.MaxTimestamp)
assert.Error(t, err) assert.Error(t, err)
}) })
@ -1189,16 +1198,19 @@ func TestMetaTable_RenameCollection(t *testing.T) {
aliases: newNameDb(), aliases: newNameDb(),
} }
meta.names.insert("", "old", 1) meta.names.insert("", "old", 1)
err := meta.RenameCollection(context.TODO(), "", "old", "new", typeutil.MaxTimestamp) err := meta.RenameCollection(context.TODO(), "", "old", "", "new", typeutil.MaxTimestamp)
assert.Error(t, err) assert.Error(t, err)
}) })
t.Run("new collection name already exist-1", func(t *testing.T) { t.Run("new collection name already exist-1", func(t *testing.T) {
meta := &MetaTable{ meta := &MetaTable{
dbName2Meta: map[string]*model.Database{
util.DefaultDBName: model.NewDefaultDatabase(),
},
names: newNameDb(), names: newNameDb(),
aliases: newNameDb(), aliases: newNameDb(),
collID2Meta: map[typeutil.UniqueID]*model.Collection{ collID2Meta: map[typeutil.UniqueID]*model.Collection{
2: { util.DefaultDBID: {
CollectionID: 1, CollectionID: 1,
Name: "old", Name: "old",
State: pb.CollectionState_CollectionCreated, State: pb.CollectionState_CollectionCreated,
@ -1206,8 +1218,7 @@ func TestMetaTable_RenameCollection(t *testing.T) {
}, },
} }
meta.names.insert(util.DefaultDBName, "old", 1) meta.names.insert(util.DefaultDBName, "old", 1)
meta.names.insert(util.DefaultDBName, "new", 2) err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", util.DefaultDBName, "old", 1000)
err := meta.RenameCollection(context.TODO(), "", "old", "new", 1000)
assert.Error(t, err) assert.Error(t, err)
}) })
@ -1229,7 +1240,7 @@ func TestMetaTable_RenameCollection(t *testing.T) {
} }
meta.names.insert(util.DefaultDBName, "old", 1) meta.names.insert(util.DefaultDBName, "old", 1)
meta.names.insert(util.DefaultDBName, "new", 2) meta.names.insert(util.DefaultDBName, "new", 2)
err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "new", 1000) err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "", "new", 1000)
assert.Error(t, err) assert.Error(t, err)
}) })
@ -1264,7 +1275,37 @@ func TestMetaTable_RenameCollection(t *testing.T) {
}, },
} }
meta.names.insert(util.DefaultDBName, "old", 1) meta.names.insert(util.DefaultDBName, "old", 1)
err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "new", 1000) err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "", "new", 1000)
assert.Error(t, err)
})
t.Run("rename db name fails if aliases exists", func(t *testing.T) {
catalog := mocks.NewRootCoordCatalog(t)
catalog.On("GetCollectionByName",
mock.Anything,
mock.Anything,
mock.Anything,
mock.Anything,
).Return(nil, common.NewCollectionNotExistError("error"))
meta := &MetaTable{
dbName2Meta: map[string]*model.Database{
util.DefaultDBName: model.NewDefaultDatabase(),
"db1": model.NewDatabase(2, "db1", pb.DatabaseState_DatabaseCreated),
},
catalog: catalog,
names: newNameDb(),
aliases: newNameDb(),
collID2Meta: map[typeutil.UniqueID]*model.Collection{
1: {
CollectionID: 1,
Name: "old",
},
},
}
meta.names.insert(util.DefaultDBName, "old", 1)
meta.aliases.insert(util.DefaultDBName, "alias", 1)
err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "db1", "new", 1000)
assert.Error(t, err) assert.Error(t, err)
}) })
@ -1298,7 +1339,7 @@ func TestMetaTable_RenameCollection(t *testing.T) {
}, },
} }
meta.names.insert(util.DefaultDBName, "old", 1) meta.names.insert(util.DefaultDBName, "old", 1)
err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "new", 1000) err := meta.RenameCollection(context.TODO(), util.DefaultDBName, "old", "", "new", 1000)
assert.NoError(t, err) assert.NoError(t, err)
id, ok := meta.names.get(util.DefaultDBName, "new") id, ok := meta.names.get(util.DefaultDBName, "new")

View File

@ -155,7 +155,7 @@ func (m mockMetaTable) AlterCollection(ctx context.Context, oldColl *model.Colle
return m.AlterCollectionFunc(ctx, oldColl, newColl, ts) return m.AlterCollectionFunc(ctx, oldColl, newColl, ts)
} }
func (m *mockMetaTable) RenameCollection(ctx context.Context, dbName string, oldName string, newName string, ts Timestamp) error { func (m *mockMetaTable) RenameCollection(ctx context.Context, dbName string, oldName string, newDBName string, newName string, ts Timestamp) error {
return m.RenameCollectionFunc(ctx, oldName, newName, ts) return m.RenameCollectionFunc(ctx, oldName, newName, ts)
} }

View File

@ -1569,12 +1569,12 @@ func (_c *IMetaTable_RemovePartition_Call) Return(_a0 error) *IMetaTable_RemoveP
} }
// RenameCollection provides a mock function with given fields: ctx, dbName, oldName, newName, ts // RenameCollection provides a mock function with given fields: ctx, dbName, oldName, newName, ts
func (_m *IMetaTable) RenameCollection(ctx context.Context, dbName string, oldName string, newName string, ts uint64) error { func (_m *IMetaTable) RenameCollection(ctx context.Context, dbName string, oldName string, newDBName string, newName string, ts uint64) error {
ret := _m.Called(ctx, dbName, oldName, newName, ts) ret := _m.Called(ctx, dbName, oldName, newDBName, newName, ts)
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, uint64) error); ok { if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, uint64) error); ok {
r0 = rf(ctx, dbName, oldName, newName, ts) r0 = rf(ctx, dbName, oldName, newDBName, newName, ts)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }

View File

@ -39,5 +39,5 @@ func (t *renameCollectionTask) Execute(ctx context.Context) error {
if err := t.core.ExpireMetaCache(ctx, t.Req.GetDbName(), []string{t.Req.GetOldName()}, InvalidCollectionID, t.GetTs()); err != nil { if err := t.core.ExpireMetaCache(ctx, t.Req.GetDbName(), []string{t.Req.GetOldName()}, InvalidCollectionID, t.GetTs()); err != nil {
return err return err
} }
return t.core.meta.RenameCollection(ctx, t.Req.GetDbName(), t.Req.GetOldName(), t.Req.GetNewName(), t.GetTs()) return t.core.meta.RenameCollection(ctx, t.Req.GetDbName(), t.Req.GetOldName(), t.Req.GetNewDBName(), t.Req.GetNewName(), t.GetTs())
} }