2021-10-29 13:13:15 +08:00
|
|
|
// 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
|
2021-04-19 11:13:52 +08:00
|
|
|
// with the License. You may obtain a copy of the License at
|
|
|
|
//
|
2021-10-29 13:13:15 +08:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2021-04-19 11:13:52 +08:00
|
|
|
//
|
2021-10-29 13:13:15 +08:00
|
|
|
// 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.
|
2021-04-19 11:13:52 +08:00
|
|
|
|
2021-01-11 18:35:54 +08:00
|
|
|
package rocksdbkv
|
|
|
|
|
|
|
|
import (
|
2021-09-22 17:21:00 +08:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2021-12-17 23:44:42 +08:00
|
|
|
"github.com/milvus-io/milvus/internal/util/typeutil"
|
2021-01-11 18:35:54 +08:00
|
|
|
"github.com/tecbot/gorocksdb"
|
|
|
|
)
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// RocksdbKV is KV implemented by rocksdb
|
2021-01-11 18:35:54 +08:00
|
|
|
type RocksdbKV struct {
|
2021-08-16 18:46:10 +08:00
|
|
|
Opts *gorocksdb.Options
|
|
|
|
DB *gorocksdb.DB
|
|
|
|
WriteOptions *gorocksdb.WriteOptions
|
|
|
|
ReadOptions *gorocksdb.ReadOptions
|
2021-01-11 18:35:54 +08:00
|
|
|
name string
|
|
|
|
}
|
|
|
|
|
2021-09-30 22:40:14 +08:00
|
|
|
const (
|
2021-12-07 14:47:37 +08:00
|
|
|
// LRUCacheSize is the lru cache size of rocksdb, default 0
|
2021-10-13 20:28:34 +08:00
|
|
|
LRUCacheSize = 0
|
2021-09-30 22:40:14 +08:00
|
|
|
)
|
|
|
|
|
2021-12-17 23:44:42 +08:00
|
|
|
// NewRocksdbKV returns a rockskv object, only used in test
|
2021-01-11 18:35:54 +08:00
|
|
|
func NewRocksdbKV(name string) (*RocksdbKV, error) {
|
2021-12-17 23:44:42 +08:00
|
|
|
// TODO we should use multiple column family of rocks db rather than init multiple db instance
|
2021-09-22 17:21:00 +08:00
|
|
|
if name == "" {
|
|
|
|
return nil, errors.New("rocksdb name is nil")
|
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
bbto := gorocksdb.NewDefaultBlockBasedTableOptions()
|
2021-09-29 18:45:59 +08:00
|
|
|
bbto.SetCacheIndexAndFilterBlocks(true)
|
2021-10-08 10:19:06 +08:00
|
|
|
bbto.SetPinL0FilterAndIndexBlocksInCache(true)
|
2021-09-30 22:40:14 +08:00
|
|
|
bbto.SetBlockCache(gorocksdb.NewLRUCache(LRUCacheSize))
|
2021-01-11 18:35:54 +08:00
|
|
|
opts := gorocksdb.NewDefaultOptions()
|
|
|
|
opts.SetBlockBasedTableFactory(bbto)
|
2021-12-17 23:44:42 +08:00
|
|
|
// by default there are only 1 thread for flush compaction, which may block each other.
|
|
|
|
// increase to a reasonable thread numbers
|
|
|
|
opts.IncreaseParallelism(2)
|
|
|
|
// enable back ground flush
|
|
|
|
opts.SetMaxBackgroundFlushes(1)
|
2021-01-11 18:35:54 +08:00
|
|
|
opts.SetCreateIfMissing(true)
|
2021-12-17 23:44:42 +08:00
|
|
|
return NewRocksdbKVWithOpts(name, opts)
|
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
|
2021-12-17 23:44:42 +08:00
|
|
|
// NewRocksdbKV returns a rockskv object
|
|
|
|
func NewRocksdbKVWithOpts(name string, opts *gorocksdb.Options) (*RocksdbKV, error) {
|
2021-01-11 18:35:54 +08:00
|
|
|
ro := gorocksdb.NewDefaultReadOptions()
|
|
|
|
wo := gorocksdb.NewDefaultWriteOptions()
|
2021-12-17 23:44:42 +08:00
|
|
|
|
|
|
|
// only has one columnn families
|
2021-01-11 18:35:54 +08:00
|
|
|
db, err := gorocksdb.OpenDb(opts, name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &RocksdbKV{
|
2021-08-16 18:46:10 +08:00
|
|
|
Opts: opts,
|
|
|
|
DB: db,
|
|
|
|
WriteOptions: wo,
|
|
|
|
ReadOptions: ro,
|
2021-01-11 18:35:54 +08:00
|
|
|
name: name,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// Close free resource of rocksdb
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) Close() {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB != nil {
|
|
|
|
kv.DB.Close()
|
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// GetName returns the name of this object
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) GetName() string {
|
|
|
|
return kv.name
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// Load returns the value of specified key
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) Load(key string) (string, error) {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-11-16 09:19:55 +08:00
|
|
|
return "", fmt.Errorf("rocksdb instance is nil when load %s", key)
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
if key == "" {
|
|
|
|
return "", errors.New("rocksdb kv does not support load empty key")
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
value, err := kv.DB.Get(kv.ReadOptions, []byte(key))
|
2021-09-28 20:36:04 +08:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
defer value.Free()
|
2021-09-28 20:36:04 +08:00
|
|
|
return string(value.Data()), nil
|
2021-01-11 18:35:54 +08:00
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// LoadWithPrefix returns a batch values of keys with a prefix
|
2021-12-17 23:44:42 +08:00
|
|
|
// if prefix is "", then load every thing from the database
|
|
|
|
func (kv *RocksdbKV) LoadWithPrefix(prefix string) ([]string, []string, error) {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-12-17 23:44:42 +08:00
|
|
|
return nil, nil, fmt.Errorf("rocksdb instance is nil when load %s", prefix)
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
kv.ReadOptions.SetPrefixSameAsStart(true)
|
2021-12-17 23:44:42 +08:00
|
|
|
if prefix != "" {
|
|
|
|
kv.ReadOptions.SetIterateUpperBound([]byte(typeutil.AddOne(prefix)))
|
2021-01-12 18:03:24 +08:00
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
iter := kv.DB.NewIterator(kv.ReadOptions)
|
2021-01-12 18:03:24 +08:00
|
|
|
defer iter.Close()
|
2021-01-11 18:35:54 +08:00
|
|
|
keys := make([]string, 0)
|
|
|
|
values := make([]string, 0)
|
2021-12-17 23:44:42 +08:00
|
|
|
iter.Seek([]byte(prefix))
|
2021-01-11 18:35:54 +08:00
|
|
|
for ; iter.Valid(); iter.Next() {
|
|
|
|
key := iter.Key()
|
|
|
|
value := iter.Value()
|
|
|
|
keys = append(keys, string(key.Data()))
|
|
|
|
values = append(values, string(value.Data()))
|
|
|
|
key.Free()
|
|
|
|
value.Free()
|
|
|
|
}
|
|
|
|
if err := iter.Err(); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return keys, values, nil
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// MultiLoad load a batch of values by keys
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) MultiLoad(keys []string) ([]string, error) {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-11-17 22:01:31 +08:00
|
|
|
return nil, errors.New("rocksdb instance is nil when do MultiLoad")
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
values := make([]string, 0, len(keys))
|
|
|
|
for _, key := range keys {
|
2021-08-16 18:46:10 +08:00
|
|
|
value, err := kv.DB.Get(kv.ReadOptions, []byte(key))
|
2021-01-11 18:35:54 +08:00
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
values = append(values, string(value.Data()))
|
2021-09-28 19:36:04 +08:00
|
|
|
value.Free()
|
2021-01-11 18:35:54 +08:00
|
|
|
}
|
|
|
|
return values, nil
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// Save a pair of key-value
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) Save(key, value string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-11-17 22:03:24 +08:00
|
|
|
return errors.New("rocksdb instance is nil when do save")
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
if key == "" {
|
|
|
|
return errors.New("rocksdb kv does not support empty key")
|
|
|
|
}
|
|
|
|
if value == "" {
|
|
|
|
return errors.New("rocksdb kv does not support empty value")
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
err := kv.DB.Put(kv.WriteOptions, []byte(key), []byte(value))
|
2021-01-11 18:35:54 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// MultiSave a batch of key-values
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) MultiSave(kvs map[string]string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-12-10 10:06:31 +08:00
|
|
|
return errors.New("rocksdb instance is nil when do MultiSave")
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
writeBatch := gorocksdb.NewWriteBatch()
|
2021-09-28 19:36:04 +08:00
|
|
|
defer writeBatch.Destroy()
|
2021-01-11 18:35:54 +08:00
|
|
|
for k, v := range kvs {
|
|
|
|
writeBatch.Put([]byte(k), []byte(v))
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
err := kv.DB.Write(kv.WriteOptions, writeBatch)
|
2021-01-11 18:35:54 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// RemoveWithPrefix removes a batch of key-values with specified prefix
|
2021-12-17 23:44:42 +08:00
|
|
|
// If prefix is "", then all data in the rocksdb kv will be deleted
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) RemoveWithPrefix(prefix string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-12-10 10:08:45 +08:00
|
|
|
return errors.New("rocksdb instance is nil when do RemoveWithPrefix")
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
if len(prefix) == 0 {
|
|
|
|
// better to use drop column family, but as we use default column family, we just delete ["",lastKey+1)
|
|
|
|
readOpts := gorocksdb.NewDefaultReadOptions()
|
|
|
|
defer readOpts.Destroy()
|
|
|
|
iter := kv.DB.NewIterator(readOpts)
|
|
|
|
defer iter.Close()
|
|
|
|
// seek to the last key
|
|
|
|
iter.SeekToLast()
|
|
|
|
if iter.Valid() {
|
|
|
|
return kv.DeleteRange(prefix, typeutil.AddOne(string(iter.Key().Data())))
|
2021-01-11 18:35:54 +08:00
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
// nothing in the range, skip
|
|
|
|
return nil
|
2021-01-11 18:35:54 +08:00
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
prefixEnd := typeutil.AddOne(prefix)
|
|
|
|
return kv.DeleteRange(prefix, prefixEnd)
|
2021-01-11 18:35:54 +08:00
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// Remove is used to remove a pair of key-value
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) Remove(key string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-12-10 21:14:14 +08:00
|
|
|
return errors.New("rocksdb instance is nil when do Remove")
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
if key == "" {
|
|
|
|
return errors.New("rocksdb kv does not support empty key")
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
err := kv.DB.Delete(kv.WriteOptions, []byte(key))
|
2021-01-11 18:35:54 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// MultiRemove is used to remove a batch of key-values
|
2021-01-11 18:35:54 +08:00
|
|
|
func (kv *RocksdbKV) MultiRemove(keys []string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
2021-12-10 21:16:04 +08:00
|
|
|
return errors.New("rocksdb instance is nil when do MultiRemove")
|
2021-09-22 17:21:00 +08:00
|
|
|
}
|
2021-01-11 18:35:54 +08:00
|
|
|
writeBatch := gorocksdb.NewWriteBatch()
|
2021-09-28 19:36:04 +08:00
|
|
|
defer writeBatch.Destroy()
|
2021-01-11 18:35:54 +08:00
|
|
|
for _, key := range keys {
|
|
|
|
writeBatch.Delete([]byte(key))
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
err := kv.DB.Write(kv.WriteOptions, writeBatch)
|
2021-01-11 18:35:54 +08:00
|
|
|
return err
|
|
|
|
}
|
2021-02-09 15:57:10 +08:00
|
|
|
|
2021-11-18 19:27:13 +08:00
|
|
|
// MultiSaveAndRemove provides a transaction to execute a batch of operations
|
2021-02-09 15:57:10 +08:00
|
|
|
func (kv *RocksdbKV) MultiSaveAndRemove(saves map[string]string, removals []string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
|
|
|
return errors.New("Rocksdb instance is nil when do MultiSaveAndRemove")
|
|
|
|
}
|
2021-02-09 15:57:10 +08:00
|
|
|
writeBatch := gorocksdb.NewWriteBatch()
|
2021-09-28 19:36:04 +08:00
|
|
|
defer writeBatch.Destroy()
|
2021-02-09 15:57:10 +08:00
|
|
|
for k, v := range saves {
|
|
|
|
writeBatch.Put([]byte(k), []byte(v))
|
|
|
|
}
|
|
|
|
for _, key := range removals {
|
|
|
|
writeBatch.Delete([]byte(key))
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
err := kv.DB.Write(kv.WriteOptions, writeBatch)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// DeleteRange remove a batch of key-values from startKey to endKey
|
2021-08-16 18:46:10 +08:00
|
|
|
func (kv *RocksdbKV) DeleteRange(startKey, endKey string) error {
|
2021-09-22 17:21:00 +08:00
|
|
|
if kv.DB == nil {
|
|
|
|
return errors.New("Rocksdb instance is nil when do DeleteRange")
|
|
|
|
}
|
2021-12-17 23:44:42 +08:00
|
|
|
if startKey >= endKey {
|
|
|
|
return fmt.Errorf("rockskv delete range startkey must < endkey, startkey %s, endkey %s", startKey, endKey)
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
writeBatch := gorocksdb.NewWriteBatch()
|
2021-09-28 19:36:04 +08:00
|
|
|
defer writeBatch.Destroy()
|
2021-08-16 18:46:10 +08:00
|
|
|
writeBatch.DeleteRange([]byte(startKey), []byte(endKey))
|
|
|
|
err := kv.DB.Write(kv.WriteOptions, writeBatch)
|
2021-02-09 15:57:10 +08:00
|
|
|
return err
|
|
|
|
}
|
2021-03-06 16:00:41 +08:00
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// MultiRemoveWithPrefix is used to remove a batch of key-values with the same prefix
|
2021-03-06 16:00:41 +08:00
|
|
|
func (kv *RocksdbKV) MultiRemoveWithPrefix(keys []string) error {
|
|
|
|
panic("not implement")
|
|
|
|
}
|
2021-08-16 18:46:10 +08:00
|
|
|
|
2021-09-28 08:28:02 +08:00
|
|
|
// MultiSaveAndRemoveWithPrefix is used to execute a batch operators with the same prefix
|
2021-03-06 16:00:41 +08:00
|
|
|
func (kv *RocksdbKV) MultiSaveAndRemoveWithPrefix(saves map[string]string, removals []string) error {
|
|
|
|
panic("not implement")
|
|
|
|
}
|