218 lines
5.7 KiB
Go
218 lines
5.7 KiB
Go
package leveldb
|
|
|
|
import (
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
|
dberr "github.com/syndtr/goleveldb/leveldb/errors"
|
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
|
"go.uber.org/zap"
|
|
|
|
"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/pkg/storage"
|
|
)
|
|
|
|
// storageImpl implements the db.Database interface.
|
|
// It uses a LevelDB database to store key-value pairs.
|
|
type storageImpl struct {
|
|
// The LevelDB database instance.
|
|
db *leveldb.DB
|
|
transaction *leveldb.Transaction
|
|
}
|
|
|
|
// NewDiskStorage creates a new instance of the storageImpl.
|
|
// It opens the database file at the specified path and returns an error if it fails.
|
|
func NewDiskStorage(provider LevelDBConfigurationProvider, logger *zap.Logger) storage.Storage {
|
|
logger = logger.Named("leveldb")
|
|
|
|
if provider == nil {
|
|
log.Fatal("NewDiskStorage: missing LevelDB configuration provider\n")
|
|
}
|
|
if provider.GetDBPath() == "" {
|
|
log.Fatal("NewDiskStorage: cannot have empty filepath for the database\n")
|
|
}
|
|
if provider.GetDBName() == "" {
|
|
log.Fatal("NewDiskStorage: cannot have empty db name for the database\n")
|
|
}
|
|
|
|
o := &opt.Options{
|
|
Filter: filter.NewBloomFilter(10),
|
|
}
|
|
|
|
filePath := provider.GetDBPath() + "/" + provider.GetDBName()
|
|
|
|
db, err := leveldb.OpenFile(filePath, o)
|
|
if err != nil {
|
|
log.Fatalf("NewDiskStorage: failed loading up key value storer adapter at %v with error: %v\n", filePath, err)
|
|
}
|
|
return &storageImpl{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// Get retrieves a value from the database by its key.
|
|
// It returns an error if the key is not found.
|
|
func (impl *storageImpl) Get(k string) ([]byte, error) {
|
|
if impl.transaction == nil {
|
|
bin, err := impl.db.Get([]byte(k), nil)
|
|
if err == dberr.ErrNotFound {
|
|
return nil, nil
|
|
}
|
|
return bin, nil
|
|
}
|
|
|
|
bin, err := impl.transaction.Get([]byte(k), nil)
|
|
if err == dberr.ErrNotFound {
|
|
return nil, nil
|
|
}
|
|
return bin, nil
|
|
}
|
|
|
|
// Set sets a value in the database by its key.
|
|
// It returns an error if the operation fails.
|
|
func (impl *storageImpl) Set(k string, val []byte) error {
|
|
if impl.transaction == nil {
|
|
impl.db.Delete([]byte(k), nil)
|
|
err := impl.db.Put([]byte(k), val, nil)
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
impl.transaction.Delete([]byte(k), nil)
|
|
err := impl.transaction.Put([]byte(k), val, nil)
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Delete deletes a value from the database by its key.
|
|
// It returns an error if the operation fails.
|
|
func (impl *storageImpl) Delete(k string) error {
|
|
if impl.transaction == nil {
|
|
err := impl.db.Delete([]byte(k), nil)
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
err := impl.transaction.Delete([]byte(k), nil)
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Iterate iterates over the key-value pairs in the database, starting from the specified key prefix.
|
|
// It calls the provided function for each pair.
|
|
// It returns an error if the iteration fails.
|
|
func (impl *storageImpl) Iterate(processFunc func(key, value []byte) error) error {
|
|
if impl.transaction == nil {
|
|
iter := impl.db.NewIterator(nil, nil)
|
|
for ok := iter.First(); ok; ok = iter.Next() {
|
|
// Call the passed function for each key-value pair.
|
|
err := processFunc(iter.Key(), iter.Value())
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err // Exit early if the processing function returns an error.
|
|
}
|
|
}
|
|
iter.Release()
|
|
return iter.Error()
|
|
}
|
|
|
|
iter := impl.transaction.NewIterator(nil, nil)
|
|
for ok := iter.First(); ok; ok = iter.Next() {
|
|
// Call the passed function for each key-value pair.
|
|
err := processFunc(iter.Key(), iter.Value())
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err // Exit early if the processing function returns an error.
|
|
}
|
|
}
|
|
iter.Release()
|
|
return iter.Error()
|
|
}
|
|
|
|
func (impl *storageImpl) IterateWithFilterByKeys(ks []string, processFunc func(key, value []byte) error) error {
|
|
if impl.transaction == nil {
|
|
iter := impl.db.NewIterator(nil, nil)
|
|
for ok := iter.First(); ok; ok = iter.Next() {
|
|
// Iterate over our keys to search by.
|
|
for _, k := range ks {
|
|
searchKey := strings.ToLower(k)
|
|
targetKey := strings.ToLower(string(iter.Key()))
|
|
|
|
// If the item we currently have matches our keys then execute.
|
|
if searchKey == targetKey {
|
|
// Call the passed function for each key-value pair.
|
|
err := processFunc(iter.Key(), iter.Value())
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err // Exit early if the processing function returns an error.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
iter.Release()
|
|
return iter.Error()
|
|
}
|
|
|
|
iter := impl.transaction.NewIterator(nil, nil)
|
|
for ok := iter.First(); ok; ok = iter.Next() {
|
|
// Call the passed function for each key-value pair.
|
|
err := processFunc(iter.Key(), iter.Value())
|
|
if err == dberr.ErrNotFound {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err // Exit early if the processing function returns an error.
|
|
}
|
|
}
|
|
iter.Release()
|
|
return iter.Error()
|
|
}
|
|
|
|
// Close closes the database.
|
|
// It returns an error if the operation fails.
|
|
func (impl *storageImpl) Close() error {
|
|
if impl.transaction != nil {
|
|
impl.transaction.Discard()
|
|
}
|
|
return impl.db.Close()
|
|
}
|
|
|
|
func (impl *storageImpl) OpenTransaction() error {
|
|
transaction, err := impl.db.OpenTransaction()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
impl.transaction = transaction
|
|
return nil
|
|
}
|
|
|
|
func (impl *storageImpl) CommitTransaction() error {
|
|
defer func() {
|
|
impl.transaction = nil
|
|
}()
|
|
|
|
// Commit the snapshot to the database
|
|
return impl.transaction.Commit()
|
|
}
|
|
|
|
func (impl *storageImpl) DiscardTransaction() {
|
|
defer func() {
|
|
impl.transaction = nil
|
|
}()
|
|
impl.transaction.Discard()
|
|
}
|