-
Notifications
You must be signed in to change notification settings - Fork 721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
keyspace, api: support the keyspace group split #6293
Changes from all commits
0949bee
a9a4157
34d465d
5246b7b
3544070
787417b
03dcbcb
dac4b7b
744c304
2c58564
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,7 +116,7 @@ func (m *GroupManager) GetKeyspaceGroups(startID uint32, limit int) ([]*endpoint | |
return m.store.LoadKeyspaceGroups(startID, limit) | ||
} | ||
|
||
// GetKeyspaceGroupByID returns the keyspace group by id. | ||
// GetKeyspaceGroupByID returns the keyspace group by ID. | ||
func (m *GroupManager) GetKeyspaceGroupByID(id uint32) (*endpoint.KeyspaceGroup, error) { | ||
var ( | ||
kg *endpoint.KeyspaceGroup | ||
|
@@ -135,7 +135,7 @@ func (m *GroupManager) GetKeyspaceGroupByID(id uint32) (*endpoint.KeyspaceGroup, | |
return kg, nil | ||
} | ||
|
||
// DeleteKeyspaceGroupByID deletes the keyspace group by id. | ||
// DeleteKeyspaceGroupByID deletes the keyspace group by ID. | ||
func (m *GroupManager) DeleteKeyspaceGroupByID(id uint32) (*endpoint.KeyspaceGroup, error) { | ||
var ( | ||
kg *endpoint.KeyspaceGroup | ||
|
@@ -165,15 +165,11 @@ func (m *GroupManager) DeleteKeyspaceGroupByID(id uint32) (*endpoint.KeyspaceGro | |
return kg, nil | ||
} | ||
|
||
// saveKeyspaceGroups will try to save the given keyspace groups into the storage. | ||
// If any keyspace group already exists and `overwrite` is false, it will return ErrKeyspaceGroupExists. | ||
func (m *GroupManager) saveKeyspaceGroups(keyspaceGroups []*endpoint.KeyspaceGroup, overwrite bool) error { | ||
return m.store.RunInTxn(m.ctx, func(txn kv.Txn) error { | ||
for _, keyspaceGroup := range keyspaceGroups { | ||
// TODO: add replica count | ||
newKG := &endpoint.KeyspaceGroup{ | ||
ID: keyspaceGroup.ID, | ||
UserKind: keyspaceGroup.UserKind, | ||
Keyspaces: keyspaceGroup.Keyspaces, | ||
} | ||
// Check if keyspace group has already existed. | ||
oldKG, err := m.store.LoadKeyspaceGroup(txn, keyspaceGroup.ID) | ||
if err != nil { | ||
|
@@ -182,13 +178,19 @@ func (m *GroupManager) saveKeyspaceGroups(keyspaceGroups []*endpoint.KeyspaceGro | |
if oldKG != nil && !overwrite { | ||
return ErrKeyspaceGroupExists | ||
} | ||
m.store.SaveKeyspaceGroup(txn, newKG) | ||
m.store.SaveKeyspaceGroup(txn, &endpoint.KeyspaceGroup{ | ||
ID: keyspaceGroup.ID, | ||
UserKind: keyspaceGroup.UserKind, | ||
Members: keyspaceGroup.Members, | ||
Keyspaces: keyspaceGroup.Keyspaces, | ||
InSplit: keyspaceGroup.InSplit, | ||
}) | ||
} | ||
return nil | ||
}) | ||
} | ||
|
||
// GetAvailableKeyspaceGroupIDByKind returns the available keyspace group id by user kind. | ||
// GetAvailableKeyspaceGroupIDByKind returns the available keyspace group ID by user kind. | ||
func (m *GroupManager) GetAvailableKeyspaceGroupIDByKind(userKind endpoint.UserKind) (string, error) { | ||
m.RLock() | ||
defer m.RUnlock() | ||
|
@@ -278,3 +280,86 @@ func (m *GroupManager) UpdateKeyspaceGroup(oldGroupID, newGroupID string, oldUse | |
|
||
return nil | ||
} | ||
|
||
// SplitKeyspaceGroupByID splits the keyspace group by ID into a new keyspace group with the given new ID. | ||
// And the keyspaces in the old keyspace group will be moved to the new keyspace group. | ||
func (m *GroupManager) SplitKeyspaceGroupByID(id, newID uint32, keyspaces []uint32) error { | ||
// TODO: avoid to split when the keyspaces is empty. | ||
return m.store.RunInTxn(m.ctx, func(txn kv.Txn) error { | ||
// Load the old keyspace group first. | ||
oldKg, err := m.store.LoadKeyspaceGroup(txn, id) | ||
if err != nil { | ||
return err | ||
} | ||
if oldKg == nil { | ||
return ErrKeyspaceGroupNotFound | ||
} | ||
// Check if the new keyspace group already exists. | ||
newKg, err := m.store.LoadKeyspaceGroup(txn, newID) | ||
if err != nil { | ||
return err | ||
} | ||
if newKg != nil { | ||
return ErrKeyspaceGroupExists | ||
} | ||
// Check if the keyspaces are all in the old keyspace group. | ||
if len(keyspaces) > len(oldKg.Keyspaces) { | ||
return ErrKeyspaceNotInKeyspaceGroup | ||
} | ||
var ( | ||
oldKeyspaceMap = make(map[uint32]struct{}, len(oldKg.Keyspaces)) | ||
newKeyspaceMap = make(map[uint32]struct{}, len(keyspaces)) | ||
) | ||
for _, keyspace := range oldKg.Keyspaces { | ||
oldKeyspaceMap[keyspace] = struct{}{} | ||
} | ||
for _, keyspace := range keyspaces { | ||
if _, ok := oldKeyspaceMap[keyspace]; !ok { | ||
return ErrKeyspaceNotInKeyspaceGroup | ||
} | ||
newKeyspaceMap[keyspace] = struct{}{} | ||
} | ||
// Get the split keyspace group for the old keyspace group. | ||
splitKeyspaces := make([]uint32, 0, len(oldKg.Keyspaces)-len(keyspaces)) | ||
for _, keyspace := range oldKg.Keyspaces { | ||
if _, ok := newKeyspaceMap[keyspace]; !ok { | ||
splitKeyspaces = append(splitKeyspaces, keyspace) | ||
} | ||
} | ||
// Update the old keyspace group. | ||
oldKg.Keyspaces = splitKeyspaces | ||
if err = m.store.SaveKeyspaceGroup(txn, oldKg); err != nil { | ||
return err | ||
} | ||
// Create the new split keyspace group. | ||
return m.store.SaveKeyspaceGroup(txn, &endpoint.KeyspaceGroup{ | ||
ID: newID, | ||
// Keep the same user kind and members as the old keyspace group. | ||
UserKind: oldKg.UserKind, | ||
Members: oldKg.Members, | ||
Keyspaces: keyspaces, | ||
// Only set the new keyspace group in split state. | ||
InSplit: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need one more field to denote this keyspace group is split from which keyspace group? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At present, I haven't thought of any scenarios where this field would be useful. Perhaps we can add it when we actually need it. |
||
}) | ||
}) | ||
} | ||
|
||
// FinishSplitKeyspaceByID finishes the split keyspace group by ID. | ||
func (m *GroupManager) FinishSplitKeyspaceByID(id uint32) error { | ||
return m.store.RunInTxn(m.ctx, func(txn kv.Txn) error { | ||
// Load the keyspace group first. | ||
kg, err := m.store.LoadKeyspaceGroup(txn, id) | ||
if err != nil { | ||
return err | ||
} | ||
if kg == nil { | ||
return ErrKeyspaceGroupNotFound | ||
} | ||
// Check if it's in the split state. | ||
if !kg.InSplit { | ||
return ErrKeyspaceGroupNotInSplit | ||
} | ||
kg.InSplit = false | ||
return m.store.SaveKeyspaceGroup(txn, kg) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though, the put events to the old and new groups will be propagated asynchronously, we should still update/create both keyspace groups in a transaction to provide atomic operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole
SplitKeyspaceGroupByID
is in a txn.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok