github.com/status-im/status-go@v1.1.0/db/db.go (about)

     1  package db
     2  
     3  import (
     4  	"path/filepath"
     5  
     6  	"github.com/syndtr/goleveldb/leveldb"
     7  	"github.com/syndtr/goleveldb/leveldb/errors"
     8  	"github.com/syndtr/goleveldb/leveldb/iterator"
     9  	"github.com/syndtr/goleveldb/leveldb/opt"
    10  	"github.com/syndtr/goleveldb/leveldb/storage"
    11  	"github.com/syndtr/goleveldb/leveldb/util"
    12  
    13  	"github.com/ethereum/go-ethereum/log"
    14  )
    15  
    16  type storagePrefix byte
    17  
    18  const (
    19  	// PeersCache is used for the db entries used for peers DB
    20  	PeersCache storagePrefix = iota
    21  	// DeduplicatorCache is used for the db entries used for messages
    22  	// deduplication cache
    23  	DeduplicatorCache
    24  	// MailserversCache is a list of mail servers provided by users.
    25  	MailserversCache
    26  	// TopicHistoryBucket isolated bucket for storing history metadata.
    27  	TopicHistoryBucket
    28  	// HistoryRequestBucket isolated bucket for storing list of pending requests.
    29  	HistoryRequestBucket
    30  )
    31  
    32  // NewMemoryDB returns leveldb with memory backend prefixed with a bucket.
    33  func NewMemoryDB() (*leveldb.DB, error) {
    34  	return leveldb.Open(storage.NewMemStorage(), nil)
    35  }
    36  
    37  // NewDBNamespace returns instance that ensures isolated operations.
    38  func NewDBNamespace(db Storage, prefix storagePrefix) LevelDBNamespace {
    39  	return LevelDBNamespace{
    40  		db:     db,
    41  		prefix: prefix,
    42  	}
    43  }
    44  
    45  // NewMemoryDBNamespace wraps in memory leveldb with provided bucket.
    46  // Mostly used for tests. Including tests in other packages.
    47  func NewMemoryDBNamespace(prefix storagePrefix) (pdb LevelDBNamespace, err error) {
    48  	db, err := NewMemoryDB()
    49  	if err != nil {
    50  		return pdb, err
    51  	}
    52  	return NewDBNamespace(LevelDBStorage{db: db}, prefix), nil
    53  }
    54  
    55  // Key creates a DB key for a specified service with specified data
    56  func Key(prefix storagePrefix, data ...[]byte) []byte {
    57  	keyLength := 1
    58  	for _, d := range data {
    59  		keyLength += len(d)
    60  	}
    61  	key := make([]byte, keyLength)
    62  	key[0] = byte(prefix)
    63  	startPos := 1
    64  	for _, d := range data {
    65  		copy(key[startPos:], d[:])
    66  		startPos += len(d)
    67  	}
    68  
    69  	return key
    70  }
    71  
    72  // Create returns status pointer to leveldb.DB.
    73  func Create(path, dbName string) (*leveldb.DB, error) {
    74  	// Create euphemeral storage if the node config path isn't provided
    75  	if path == "" {
    76  		return leveldb.Open(storage.NewMemStorage(), nil)
    77  	}
    78  
    79  	path = filepath.Join(path, dbName)
    80  	return Open(path, &opt.Options{OpenFilesCacheCapacity: 5})
    81  }
    82  
    83  // Open opens an existing leveldb database
    84  func Open(path string, opts *opt.Options) (db *leveldb.DB, err error) {
    85  	db, err = leveldb.OpenFile(path, opts)
    86  	if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
    87  		log.Info("database is corrupted trying to recover", "path", path)
    88  		db, err = leveldb.RecoverFile(path, nil)
    89  	}
    90  	return
    91  }
    92  
    93  // LevelDBNamespace database where all operations will be prefixed with a certain bucket.
    94  type LevelDBNamespace struct {
    95  	db     Storage
    96  	prefix storagePrefix
    97  }
    98  
    99  func (db LevelDBNamespace) prefixedKey(key []byte) []byte {
   100  	endkey := make([]byte, len(key)+1)
   101  	endkey[0] = byte(db.prefix)
   102  	copy(endkey[1:], key)
   103  	return endkey
   104  }
   105  
   106  func (db LevelDBNamespace) Put(key, value []byte) error {
   107  	return db.db.Put(db.prefixedKey(key), value)
   108  }
   109  
   110  func (db LevelDBNamespace) Get(key []byte) ([]byte, error) {
   111  	return db.db.Get(db.prefixedKey(key))
   112  }
   113  
   114  // Range returns leveldb util.Range prefixed with a single byte.
   115  // If prefix is nil range will iterate over all records in a given bucket.
   116  func (db LevelDBNamespace) Range(prefix, limit []byte) *util.Range {
   117  	if limit == nil {
   118  		return util.BytesPrefix(db.prefixedKey(prefix))
   119  	}
   120  	return &util.Range{Start: db.prefixedKey(prefix), Limit: db.prefixedKey(limit)}
   121  }
   122  
   123  // Delete removes key from database.
   124  func (db LevelDBNamespace) Delete(key []byte) error {
   125  	return db.db.Delete(db.prefixedKey(key))
   126  }
   127  
   128  // NewIterator returns iterator for a given slice.
   129  func (db LevelDBNamespace) NewIterator(slice *util.Range) NamespaceIterator {
   130  	return NamespaceIterator{db.db.NewIterator(slice)}
   131  }
   132  
   133  // NamespaceIterator wraps leveldb iterator, works mostly the same way.
   134  // The only difference is that first byte of the key is dropped.
   135  type NamespaceIterator struct {
   136  	iter iterator.Iterator
   137  }
   138  
   139  // Key returns key of the current item.
   140  func (iter NamespaceIterator) Key() []byte {
   141  	return iter.iter.Key()[1:]
   142  }
   143  
   144  // Value returns actual value of the current item.
   145  func (iter NamespaceIterator) Value() []byte {
   146  	return iter.iter.Value()
   147  }
   148  
   149  // Error returns accumulated error.
   150  func (iter NamespaceIterator) Error() error {
   151  	return iter.iter.Error()
   152  }
   153  
   154  // Prev moves cursor backward.
   155  func (iter NamespaceIterator) Prev() bool {
   156  	return iter.iter.Prev()
   157  }
   158  
   159  // Next moves cursor forward.
   160  func (iter NamespaceIterator) Next() bool {
   161  	return iter.iter.Next()
   162  }