github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/leveldbstore/store.go (about)

     1  // Copyright 2022 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package leveldbstore
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"github.com/ethersphere/bee/v2/pkg/storage"
    13  	"github.com/syndtr/goleveldb/leveldb"
    14  	ldbErrors "github.com/syndtr/goleveldb/leveldb/errors"
    15  	"github.com/syndtr/goleveldb/leveldb/iterator"
    16  	"github.com/syndtr/goleveldb/leveldb/opt"
    17  	ldbStorage "github.com/syndtr/goleveldb/leveldb/storage"
    18  	"github.com/syndtr/goleveldb/leveldb/util"
    19  )
    20  
    21  const separator = "/"
    22  
    23  // key returns the Item identifier for the leveldb storage.
    24  func key(item storage.Key) []byte {
    25  	return []byte(item.Namespace() + separator + item.ID())
    26  }
    27  
    28  // filters is a decorator for a slice of storage.Filters
    29  // that helps with its evaluation.
    30  type filters []storage.Filter
    31  
    32  // matchAny returns true if any of the filters match the item.
    33  func (f filters) matchAny(k string, v []byte) bool {
    34  	for _, filter := range f {
    35  		if filter(k, v) {
    36  			return true
    37  		}
    38  	}
    39  	return false
    40  }
    41  
    42  // Storer returns the underlying db store.
    43  type Storer interface {
    44  	DB() *leveldb.DB
    45  }
    46  
    47  var (
    48  	_ Storer        = (*Store)(nil)
    49  	_ storage.Store = (*Store)(nil)
    50  )
    51  
    52  type Store struct {
    53  	db   *leveldb.DB
    54  	path string
    55  }
    56  
    57  // New returns a new store the backed by leveldb.
    58  // If path == "", the leveldb will run with in memory backend storage.
    59  func New(path string, opts *opt.Options) (*Store, error) {
    60  	var (
    61  		err error
    62  		db  *leveldb.DB
    63  	)
    64  
    65  	if path == "" {
    66  		db, err = leveldb.Open(ldbStorage.NewMemStorage(), opts)
    67  	} else {
    68  		db, err = leveldb.OpenFile(path, opts)
    69  		if ldbErrors.IsCorrupted(err) && !opts.GetReadOnly() {
    70  			db, err = leveldb.RecoverFile(path, opts)
    71  		}
    72  	}
    73  
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	return &Store{
    79  		db:   db,
    80  		path: path,
    81  	}, nil
    82  }
    83  
    84  // DB implements the Storer interface.
    85  func (s *Store) DB() *leveldb.DB {
    86  	return s.db
    87  }
    88  
    89  // Close implements the storage.Store interface.
    90  func (s *Store) Close() (err error) {
    91  	return s.db.Close()
    92  }
    93  
    94  // Get implements the storage.Store interface.
    95  func (s *Store) Get(item storage.Item) error {
    96  	val, err := s.db.Get(key(item), nil)
    97  
    98  	if errors.Is(err, leveldb.ErrNotFound) {
    99  		return storage.ErrNotFound
   100  	}
   101  
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	if err = item.Unmarshal(val); err != nil {
   107  		return fmt.Errorf("failed decoding value %w", err)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // Has implements the storage.Store interface.
   114  func (s *Store) Has(k storage.Key) (bool, error) {
   115  	return s.db.Has(key(k), nil)
   116  }
   117  
   118  // GetSize implements the storage.Store interface.
   119  func (s *Store) GetSize(k storage.Key) (int, error) {
   120  	val, err := s.db.Get(key(k), nil)
   121  
   122  	if errors.Is(err, leveldb.ErrNotFound) {
   123  		return 0, storage.ErrNotFound
   124  	}
   125  
   126  	if err != nil {
   127  		return 0, err
   128  	}
   129  
   130  	return len(val), nil
   131  }
   132  
   133  // Iterate implements the storage.Store interface.
   134  func (s *Store) Iterate(q storage.Query, fn storage.IterateFn) error {
   135  	if err := q.Validate(); err != nil {
   136  		return fmt.Errorf("failed iteration: %w", err)
   137  	}
   138  
   139  	var retErr error
   140  
   141  	var iter iterator.Iterator
   142  	var prefix string
   143  
   144  	defer func() {
   145  		if iter != nil {
   146  			iter.Release()
   147  		}
   148  	}()
   149  
   150  	iterOpts := &opt.ReadOptions{
   151  		DontFillCache: true,
   152  	}
   153  
   154  	if q.PrefixAtStart {
   155  		prefix = q.Factory().Namespace()
   156  		iter = s.db.NewIterator(util.BytesPrefix([]byte(prefix)), iterOpts)
   157  		exists := iter.Seek([]byte(prefix + separator + q.Prefix))
   158  		if !exists {
   159  			return nil
   160  		}
   161  		_ = iter.Prev()
   162  	} else {
   163  		// this is a small hack to make the iteration work with the
   164  		// old implementation of statestore. this allows us to do a
   165  		// full iteration without looking at the prefix.
   166  		if q.Factory().Namespace() != "" {
   167  			prefix = q.Factory().Namespace() + separator + q.Prefix
   168  		}
   169  		iter = s.db.NewIterator(util.BytesPrefix([]byte(prefix)), iterOpts)
   170  	}
   171  
   172  	nextF := iter.Next
   173  
   174  	if q.Order == storage.KeyDescendingOrder {
   175  		nextF = func() bool {
   176  			nextF = iter.Prev
   177  			return iter.Last()
   178  		}
   179  	}
   180  
   181  	firstSkipped := !q.SkipFirst
   182  
   183  	for nextF() {
   184  		keyRaw := iter.Key()
   185  		nextKey := make([]byte, len(keyRaw))
   186  		copy(nextKey, keyRaw)
   187  
   188  		valRaw := iter.Value()
   189  		nextVal := make([]byte, len(valRaw))
   190  		copy(nextVal, valRaw)
   191  
   192  		key := strings.TrimPrefix(string(nextKey), prefix)
   193  
   194  		if filters(q.Filters).matchAny(key, nextVal) {
   195  			continue
   196  		}
   197  
   198  		if q.SkipFirst && !firstSkipped {
   199  			firstSkipped = true
   200  			continue
   201  		}
   202  
   203  		var (
   204  			res *storage.Result
   205  			err error
   206  		)
   207  
   208  		switch q.ItemProperty {
   209  		case storage.QueryItemID, storage.QueryItemSize:
   210  			res = &storage.Result{ID: key, Size: len(nextVal)}
   211  		case storage.QueryItem:
   212  			newItem := q.Factory()
   213  			err = newItem.Unmarshal(nextVal)
   214  			res = &storage.Result{ID: key, Entry: newItem}
   215  		}
   216  
   217  		if err != nil {
   218  			retErr = errors.Join(retErr, fmt.Errorf("failed unmarshaling: %w", err))
   219  			break
   220  		}
   221  
   222  		if res == nil {
   223  			retErr = errors.Join(retErr, fmt.Errorf("unknown object attribute type: %v", q.ItemProperty))
   224  			break
   225  		}
   226  
   227  		if stop, err := fn(*res); err != nil {
   228  			retErr = errors.Join(retErr, fmt.Errorf("iterate callback function errored: %w", err))
   229  			break
   230  		} else if stop {
   231  			break
   232  		}
   233  	}
   234  
   235  	if err := iter.Error(); err != nil {
   236  		retErr = errors.Join(retErr, err)
   237  	}
   238  
   239  	return retErr
   240  }
   241  
   242  // Count implements the storage.Store interface.
   243  func (s *Store) Count(key storage.Key) (int, error) {
   244  	keys := util.BytesPrefix([]byte(key.Namespace() + separator))
   245  	iter := s.db.NewIterator(keys, nil)
   246  
   247  	var c int
   248  	for iter.Next() {
   249  		c++
   250  	}
   251  
   252  	iter.Release()
   253  
   254  	return c, iter.Error()
   255  }
   256  
   257  // Put implements the storage.Store interface.
   258  func (s *Store) Put(item storage.Item) error {
   259  	value, err := item.Marshal()
   260  	if err != nil {
   261  		return fmt.Errorf("failed serializing: %w", err)
   262  	}
   263  
   264  	return s.db.Put(key(item), value, nil)
   265  }
   266  
   267  // Delete implements the storage.Store interface.
   268  func (s *Store) Delete(item storage.Item) error {
   269  	// this is a small hack to make the deletion of old entries work. As they
   270  	// don't have a namespace, we need to check for that and use the ID as key without
   271  	// the separator.
   272  	var k []byte
   273  	if item.Namespace() == "" {
   274  		k = []byte(item.ID())
   275  	} else {
   276  		k = key(item)
   277  	}
   278  
   279  	return s.db.Delete(k, nil)
   280  }