Add keyPrefix support for TiKV store (#7756)
* Add keyPrefix support for TiKV store Similar to the Redis keyPrefix feature (#7299), this adds keyPrefix support for TiKV stores to enable sharing a single TiKV cluster as metadata store for multitenant SeaweedFS clusters. Changes: - Add keyPrefix field to TikvStore struct - Update Initialize function to read keyPrefix from config - Add getKey method to prepend prefix to all keys - Update generateKey, getNameFromKey, and genDirectoryKeyPrefix methods to be store receiver methods and handle key prefixing - Update filer.toml scaffold with keyPrefix configuration option Fixes #7752 * Fix potential slice corruption in getKey method Use a new slice with proper capacity to avoid modifying the underlying array of store.keyPrefix when appending. * Add keyPrefix validation and defensive bounds check - Add validation in Initialize to reject keyPrefix longer than 256 bytes - Add bounds check in getNameFromKey to prevent panic on malformed keys * Update weed/filer/tikv/tikv_store.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update weed/command/scaffold/filer.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -399,6 +399,8 @@ enabled = false
|
|||||||
# If you have many pd address, use ',' split then:
|
# If you have many pd address, use ',' split then:
|
||||||
# pdaddrs = "pdhost1:2379, pdhost2:2379, pdhost3:2379"
|
# pdaddrs = "pdhost1:2379, pdhost2:2379, pdhost3:2379"
|
||||||
pdaddrs = "localhost:2379"
|
pdaddrs = "localhost:2379"
|
||||||
|
# prefix for filer TiKV keys, useful for sharing a TiKV cluster with multiple seaweedfs clusters
|
||||||
|
keyPrefix = ""
|
||||||
# Enable 1PC
|
# Enable 1PC
|
||||||
enable_1pc = false
|
enable_1pc = false
|
||||||
# batch delete count, default 10000 in code
|
# batch delete count, default 10000 in code
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type TikvStore struct {
|
|||||||
client *txnkv.Client
|
client *txnkv.Client
|
||||||
onePC bool
|
onePC bool
|
||||||
batchCommitSize int
|
batchCommitSize int
|
||||||
|
keyPrefix []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic APIs
|
// Basic APIs
|
||||||
@@ -47,6 +48,7 @@ func (store *TikvStore) Initialize(config util.Configuration, prefix string) err
|
|||||||
key := config.GetString(prefix + "key_path")
|
key := config.GetString(prefix + "key_path")
|
||||||
verify_cn := strings.Split(config.GetString(prefix+"verify_cn"), ",")
|
verify_cn := strings.Split(config.GetString(prefix+"verify_cn"), ",")
|
||||||
pdAddrs := strings.Split(config.GetString(prefix+"pdaddrs"), ",")
|
pdAddrs := strings.Split(config.GetString(prefix+"pdaddrs"), ",")
|
||||||
|
keyPrefix := config.GetString(prefix + "keyPrefix")
|
||||||
|
|
||||||
bdc := config.GetInt(prefix + "batchdelete_count")
|
bdc := config.GetInt(prefix + "batchdelete_count")
|
||||||
if bdc <= 0 {
|
if bdc <= 0 {
|
||||||
@@ -55,6 +57,7 @@ func (store *TikvStore) Initialize(config util.Configuration, prefix string) err
|
|||||||
|
|
||||||
store.onePC = config.GetBool(prefix + "enable_1pc")
|
store.onePC = config.GetBool(prefix + "enable_1pc")
|
||||||
store.batchCommitSize = bdc
|
store.batchCommitSize = bdc
|
||||||
|
store.keyPrefix = []byte(keyPrefix)
|
||||||
return store.initialize(ca, cert, key, verify_cn, pdAddrs)
|
return store.initialize(ca, cert, key, verify_cn, pdAddrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ func (store *TikvStore) Shutdown() {
|
|||||||
// Entry APIs
|
// Entry APIs
|
||||||
func (store *TikvStore) InsertEntry(ctx context.Context, entry *filer.Entry) error {
|
func (store *TikvStore) InsertEntry(ctx context.Context, entry *filer.Entry) error {
|
||||||
dir, name := entry.DirAndName()
|
dir, name := entry.DirAndName()
|
||||||
key := generateKey(dir, name)
|
key := store.generateKey(dir, name)
|
||||||
|
|
||||||
value, err := entry.EncodeAttributesAndChunks()
|
value, err := entry.EncodeAttributesAndChunks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -104,7 +107,7 @@ func (store *TikvStore) UpdateEntry(ctx context.Context, entry *filer.Entry) err
|
|||||||
|
|
||||||
func (store *TikvStore) FindEntry(ctx context.Context, path util.FullPath) (*filer.Entry, error) {
|
func (store *TikvStore) FindEntry(ctx context.Context, path util.FullPath) (*filer.Entry, error) {
|
||||||
dir, name := path.DirAndName()
|
dir, name := path.DirAndName()
|
||||||
key := generateKey(dir, name)
|
key := store.generateKey(dir, name)
|
||||||
|
|
||||||
txn, err := store.getTxn(ctx)
|
txn, err := store.getTxn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -139,7 +142,7 @@ func (store *TikvStore) FindEntry(ctx context.Context, path util.FullPath) (*fil
|
|||||||
|
|
||||||
func (store *TikvStore) DeleteEntry(ctx context.Context, path util.FullPath) error {
|
func (store *TikvStore) DeleteEntry(ctx context.Context, path util.FullPath) error {
|
||||||
dir, name := path.DirAndName()
|
dir, name := path.DirAndName()
|
||||||
key := generateKey(dir, name)
|
key := store.generateKey(dir, name)
|
||||||
|
|
||||||
txn, err := store.getTxn(ctx)
|
txn, err := store.getTxn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -159,7 +162,7 @@ func (store *TikvStore) DeleteEntry(ctx context.Context, path util.FullPath) err
|
|||||||
|
|
||||||
// Directory APIs
|
// Directory APIs
|
||||||
func (store *TikvStore) DeleteFolderChildren(ctx context.Context, path util.FullPath) error {
|
func (store *TikvStore) DeleteFolderChildren(ctx context.Context, path util.FullPath) error {
|
||||||
directoryPrefix := genDirectoryKeyPrefix(path, "")
|
directoryPrefix := store.genDirectoryKeyPrefix(path, "")
|
||||||
|
|
||||||
iterTxn, err := store.getTxn(ctx)
|
iterTxn, err := store.getTxn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -242,10 +245,10 @@ func (store *TikvStore) ListDirectoryEntries(ctx context.Context, dirPath util.F
|
|||||||
|
|
||||||
func (store *TikvStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (string, error) {
|
func (store *TikvStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc filer.ListEachEntryFunc) (string, error) {
|
||||||
lastFileName := ""
|
lastFileName := ""
|
||||||
directoryPrefix := genDirectoryKeyPrefix(dirPath, prefix)
|
directoryPrefix := store.genDirectoryKeyPrefix(dirPath, prefix)
|
||||||
lastFileStart := directoryPrefix
|
lastFileStart := directoryPrefix
|
||||||
if startFileName != "" {
|
if startFileName != "" {
|
||||||
lastFileStart = genDirectoryKeyPrefix(dirPath, startFileName)
|
lastFileStart = store.genDirectoryKeyPrefix(dirPath, startFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
txn, err := store.getTxn(ctx)
|
txn, err := store.getTxn(ctx)
|
||||||
@@ -265,7 +268,7 @@ func (store *TikvStore) ListDirectoryPrefixedEntries(ctx context.Context, dirPat
|
|||||||
if !bytes.HasPrefix(key, directoryPrefix) {
|
if !bytes.HasPrefix(key, directoryPrefix) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
fileName := getNameFromKey(key)
|
fileName := store.getNameFromKey(key)
|
||||||
if fileName == "" {
|
if fileName == "" {
|
||||||
if err := iter.Next(); err != nil {
|
if err := iter.Next(); err != nil {
|
||||||
break
|
break
|
||||||
@@ -418,22 +421,37 @@ func hashToBytes(dir string) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKey(dirPath, fileName string) []byte {
|
func (store *TikvStore) getKey(key []byte) []byte {
|
||||||
|
if len(store.keyPrefix) == 0 {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
result := make([]byte, len(store.keyPrefix)+len(key))
|
||||||
|
copy(result, store.keyPrefix)
|
||||||
|
copy(result[len(store.keyPrefix):], key)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *TikvStore) generateKey(dirPath, fileName string) []byte {
|
||||||
key := hashToBytes(dirPath)
|
key := hashToBytes(dirPath)
|
||||||
key = append(key, []byte(fileName)...)
|
key = append(key, []byte(fileName)...)
|
||||||
return key
|
return store.getKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNameFromKey(key []byte) string {
|
func (store *TikvStore) getNameFromKey(key []byte) string {
|
||||||
return string(key[sha1.Size:])
|
minKeyLen := len(store.keyPrefix) + sha1.Size
|
||||||
|
if len(key) < minKeyLen {
|
||||||
|
glog.Warningf("malformed key: length %d is less than minimum %d", len(key), minKeyLen)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(key[minKeyLen:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func genDirectoryKeyPrefix(fullpath util.FullPath, startFileName string) (keyPrefix []byte) {
|
func (store *TikvStore) genDirectoryKeyPrefix(fullpath util.FullPath, startFileName string) (keyPrefix []byte) {
|
||||||
keyPrefix = hashToBytes(string(fullpath))
|
keyPrefix = hashToBytes(string(fullpath))
|
||||||
if len(startFileName) > 0 {
|
if len(startFileName) > 0 {
|
||||||
keyPrefix = append(keyPrefix, []byte(startFileName)...)
|
keyPrefix = append(keyPrefix, []byte(startFileName)...)
|
||||||
}
|
}
|
||||||
return keyPrefix
|
return store.getKey(keyPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isNotExists(err error) bool {
|
func isNotExists(err error) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user