arnaucube
arnaucube/go-merkletree-iden3Public Self-hosted repository
go-merkletree-iden3/db/pebble/pebble.go
db/pebble/pebble.goRaw
  1package pebble
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6
  7	"github.com/cockroachdb/pebble"
  8	"github.com/iden3/go-merkletree/db"
  9	log "github.com/sirupsen/logrus"
 10)
 11
 12// PebbleStorage implements the db.Storage interface
 13type PebbleStorage struct {
 14	pdb    *pebble.DB
 15	prefix []byte
 16}
 17
 18// PebbleStorageTx implements the db.Tx interface
 19type PebbleStorageTx struct {
 20	// FUTURE currently Tx is using the same strategy than in MemoryDB and
 21	// LevelDB, in next iteration can be moved to Pebble Batch strategy
 22	*PebbleStorage
 23	cache db.KvMap
 24}
 25
 26// NewPebbleStorage returns a new PebbleStorage
 27func NewPebbleStorage(path string, errorIfMissing bool) (*PebbleStorage, error) {
 28	o := &pebble.Options{
 29		ErrorIfNotExists: errorIfMissing,
 30	}
 31	rdb, err := pebble.Open(path, o)
 32	if err != nil {
 33		return nil, err
 34	}
 35	return &PebbleStorage{rdb, []byte{}}, nil
 36}
 37
 38type storageInfo struct {
 39	KeyCount   int
 40	ClaimCount int
 41}
 42
 43// Info implements the method Info of the interface db.Storage
 44func (p *PebbleStorage) Info() string {
 45	keycount := 0
 46	claimcount := 0
 47	err := p.Iterate(func(key []byte, value []byte) (bool, error) {
 48		if value[0] == byte(1) {
 49			claimcount++
 50		}
 51
 52		keycount++
 53		return true, nil
 54	})
 55	if err != nil {
 56		return err.Error()
 57	}
 58	json, _ := json.MarshalIndent(
 59		storageInfo{
 60			KeyCount:   keycount,
 61			ClaimCount: claimcount,
 62		},
 63		"", "  ",
 64	)
 65	return string(json)
 66}
 67
 68// WithPrefix implements the method WithPrefix of the interface db.Storage
 69func (p *PebbleStorage) WithPrefix(prefix []byte) db.Storage {
 70	return &PebbleStorage{p.pdb, db.Concat(p.prefix, prefix)}
 71}
 72
 73// NewTx implements the method NewTx of the interface db.Storage
 74func (p *PebbleStorage) NewTx() (db.Tx, error) {
 75	return &PebbleStorageTx{p, make(db.KvMap)}, nil
 76}
 77
 78// Get retreives a value from a key in the db.Storage
 79func (p *PebbleStorage) Get(key []byte) ([]byte, error) {
 80	v, closer, err := p.pdb.Get(db.Concat(p.prefix, key[:]))
 81	if err == pebble.ErrNotFound {
 82		return nil, db.ErrNotFound
 83	}
 84	closer.Close()
 85	return v, err
 86}
 87
 88// Iterate implements the method Iterate of the interface db.Storage
 89func (p *PebbleStorage) Iterate(f func([]byte, []byte) (bool, error)) error {
 90	// NewIter already provides a point-in-time view of the current DB
 91	// state, but if is used for long term (is not the case), should use an
 92	// iterator over an snapshot:
 93	// snapshot := p.pdb.NewSnapshot()
 94	// defer snapshot.Close()
 95	// iter := snapshot.NewIter(nil)
 96	iter := p.pdb.NewIter(nil)
 97	defer iter.Close()
 98
 99	iter.First() // move the iterator to the first key/value pair
100	if len(iter.Key()) < len(p.prefix) || !bytes.Equal(iter.Key()[:len(p.prefix)], p.prefix) {
101	} else {
102		localKey := iter.Key()[len(p.prefix):]
103		if _, err := f(localKey, iter.Value()); err != nil {
104			return err
105		}
106	}
107	for iter.Next() {
108		if len(iter.Key()) < len(p.prefix) || !bytes.Equal(iter.Key()[:len(p.prefix)], p.prefix) {
109			continue
110		}
111		localKey := iter.Key()[len(p.prefix):]
112		if cont, err := f(localKey, iter.Value()); err != nil {
113			return err
114		} else if !cont {
115			break
116		}
117	}
118	return iter.Error()
119}
120
121// Get retreives a value from a key in the interface db.Tx
122func (tx *PebbleStorageTx) Get(key []byte) ([]byte, error) {
123	var err error
124
125	fullkey := db.Concat(tx.prefix, key)
126
127	if value, ok := tx.cache.Get(fullkey); ok {
128		return value, nil
129	}
130
131	value, closer, err := tx.pdb.Get(fullkey)
132	if err == pebble.ErrNotFound {
133		return nil, db.ErrNotFound
134	}
135	closer.Close()
136
137	return value, err
138}
139
140// Put saves a key:value into the db.Storage
141func (tx *PebbleStorageTx) Put(k, v []byte) {
142	tx.cache.Put(db.Concat(tx.prefix, k[:]), v)
143}
144
145// Add implements the method Add of the interface db.Tx
146func (tx *PebbleStorageTx) Add(atx db.Tx) {
147	ldbtx := atx.(*PebbleStorageTx)
148	for _, v := range ldbtx.cache {
149		tx.cache.Put(v.K, v.V)
150	}
151}
152
153// Commit implements the method Commit of the interface db.Tx
154func (tx *PebbleStorageTx) Commit() error {
155	batch := tx.PebbleStorage.pdb.NewBatch()
156	for _, v := range tx.cache {
157		batch.Set(v.K, v.V, nil)
158	}
159
160	tx.cache = nil
161	return batch.Commit(nil)
162}
163
164// Close implements the method Close of the interface db.Tx
165func (tx *PebbleStorageTx) Close() {
166	tx.cache = nil
167}
168
169// Close implements the method Close of the interface db.Storage
170func (p *PebbleStorage) Close() {
171	if err := p.pdb.Close(); err != nil {
172		panic(err)
173	}
174	log.Info("Database closed")
175}
176
177// Pebble is an extra method that returns the *pebble.DB
178func (p *PebbleStorage) Pebble() *pebble.DB {
179	return p.pdb
180}
181
182// List implements the method List of the interface db.Storage
183func (p *PebbleStorage) List(limit int) ([]db.KV, error) {
184	ret := []db.KV{}
185	err := p.Iterate(func(key []byte, value []byte) (bool, error) {
186		ret = append(ret, db.KV{K: db.Clone(key), V: db.Clone(value)})
187		if len(ret) == limit {
188			return false, nil
189		}
190		return true, nil
191	})
192	return ret, err
193}