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

     1  package mailserver
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/syndtr/goleveldb/leveldb"
     8  	"github.com/syndtr/goleveldb/leveldb/errors"
     9  	"github.com/syndtr/goleveldb/leveldb/iterator"
    10  	"github.com/syndtr/goleveldb/leveldb/util"
    11  
    12  	"github.com/ethereum/go-ethereum/log"
    13  	"github.com/ethereum/go-ethereum/rlp"
    14  
    15  	"github.com/status-im/status-go/eth-node/types"
    16  	waku "github.com/status-im/status-go/waku/common"
    17  )
    18  
    19  type LevelDB struct {
    20  	// We can't embed as there are some state problems with go-routines
    21  	ldb  *leveldb.DB
    22  	name string
    23  	done chan struct{}
    24  }
    25  
    26  type LevelDBIterator struct {
    27  	iterator.Iterator
    28  }
    29  
    30  func (i *LevelDBIterator) DBKey() (*DBKey, error) {
    31  	return &DBKey{
    32  		raw: i.Key(),
    33  	}, nil
    34  }
    35  
    36  func (i *LevelDBIterator) GetEnvelopeByTopicsMap(topics map[types.TopicType]bool) ([]byte, error) {
    37  	rawValue := make([]byte, len(i.Value()))
    38  	copy(rawValue, i.Value())
    39  
    40  	key, err := i.DBKey()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	if !topics[key.Topic()] {
    46  		return nil, nil
    47  	}
    48  
    49  	return rawValue, nil
    50  }
    51  
    52  func (i *LevelDBIterator) GetEnvelopeByBloomFilter(bloom []byte) ([]byte, error) {
    53  	var envelopeBloom []byte
    54  	rawValue := make([]byte, len(i.Value()))
    55  	copy(rawValue, i.Value())
    56  
    57  	key, err := i.DBKey()
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	if len(key.Bytes()) != DBKeyLength {
    63  		var err error
    64  		envelopeBloom, err = extractBloomFromEncodedEnvelope(rawValue)
    65  		if err != nil {
    66  			return nil, err
    67  		}
    68  	} else {
    69  		envelopeBloom = types.TopicToBloom(key.Topic())
    70  	}
    71  	if !types.BloomFilterMatch(bloom, envelopeBloom) {
    72  		return nil, nil
    73  	}
    74  	return rawValue, nil
    75  }
    76  
    77  func (i *LevelDBIterator) Release() error {
    78  	i.Iterator.Release()
    79  	return nil
    80  }
    81  
    82  func NewLevelDB(dataDir string) (*LevelDB, error) {
    83  	// Open opens an existing leveldb database
    84  	db, err := leveldb.OpenFile(dataDir, nil)
    85  	if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
    86  		log.Info("database is corrupted trying to recover", "path", dataDir)
    87  		db, err = leveldb.RecoverFile(dataDir, nil)
    88  	}
    89  
    90  	instance := LevelDB{
    91  		ldb:  db,
    92  		name: dataDir, // name is used for metrics labels
    93  		done: make(chan struct{}),
    94  	}
    95  
    96  	// initialize the metric value
    97  	instance.updateArchivedEnvelopesCount()
    98  	// checking count on every insert is inefficient
    99  	go func() {
   100  		for {
   101  			select {
   102  			case <-instance.done:
   103  				return
   104  			case <-time.After(time.Second * envelopeCountCheckInterval):
   105  				instance.updateArchivedEnvelopesCount()
   106  			}
   107  		}
   108  	}()
   109  	return &instance, err
   110  }
   111  
   112  // GetEnvelope get an envelope by its key
   113  func (db *LevelDB) GetEnvelope(key *DBKey) ([]byte, error) {
   114  	defer recoverLevelDBPanics("GetEnvelope")
   115  	return db.ldb.Get(key.Bytes(), nil)
   116  }
   117  
   118  func (db *LevelDB) updateArchivedEnvelopesCount() {
   119  	if count, err := db.envelopesCount(); err != nil {
   120  		log.Warn("db query for envelopes count failed", "err", err)
   121  	} else {
   122  		archivedEnvelopesGauge.WithLabelValues(db.name).Set(float64(count))
   123  	}
   124  }
   125  
   126  // Build iterator returns an iterator given a start/end and a cursor
   127  func (db *LevelDB) BuildIterator(query CursorQuery) (Iterator, error) {
   128  	defer recoverLevelDBPanics("BuildIterator")
   129  
   130  	i := db.ldb.NewIterator(&util.Range{Start: query.start, Limit: query.end}, nil)
   131  
   132  	envelopeQueriesCounter.WithLabelValues("unknown", "unknown").Inc()
   133  	// seek to the end as we want to return envelopes in a descending order
   134  	if len(query.cursor) == CursorLength {
   135  		i.Seek(query.cursor)
   136  	}
   137  	return &LevelDBIterator{i}, nil
   138  }
   139  
   140  // Prune removes envelopes older than time
   141  func (db *LevelDB) Prune(t time.Time, batchSize int) (int, error) {
   142  	defer recoverLevelDBPanics("Prune")
   143  
   144  	var zero types.Hash
   145  	var emptyTopic types.TopicType
   146  	kl := NewDBKey(0, emptyTopic, zero)
   147  	ku := NewDBKey(uint32(t.Unix()), emptyTopic, zero)
   148  	query := CursorQuery{
   149  		start: kl.Bytes(),
   150  		end:   ku.Bytes(),
   151  	}
   152  	i, err := db.BuildIterator(query)
   153  	if err != nil {
   154  		return 0, err
   155  	}
   156  	defer func() { _ = i.Release() }()
   157  
   158  	batch := leveldb.Batch{}
   159  	removed := 0
   160  
   161  	for i.Next() {
   162  		dbKey, err := i.DBKey()
   163  		if err != nil {
   164  			return 0, err
   165  		}
   166  
   167  		batch.Delete(dbKey.Bytes())
   168  
   169  		if batch.Len() == batchSize {
   170  			if err := db.ldb.Write(&batch, nil); err != nil {
   171  				return removed, err
   172  			}
   173  
   174  			removed = removed + batch.Len()
   175  			batch.Reset()
   176  		}
   177  	}
   178  
   179  	if batch.Len() > 0 {
   180  		if err := db.ldb.Write(&batch, nil); err != nil {
   181  			return removed, err
   182  		}
   183  
   184  		removed = removed + batch.Len()
   185  	}
   186  
   187  	return removed, nil
   188  }
   189  
   190  func (db *LevelDB) envelopesCount() (int, error) {
   191  	defer recoverLevelDBPanics("envelopesCount")
   192  	iterator, err := db.BuildIterator(CursorQuery{})
   193  	if err != nil {
   194  		return 0, err
   195  	}
   196  	// LevelDB does not have API for getting a count
   197  	var count int
   198  	for iterator.Next() {
   199  		count++
   200  	}
   201  	return count, nil
   202  }
   203  
   204  // SaveEnvelope stores an envelope in leveldb and increments the metrics
   205  func (db *LevelDB) SaveEnvelope(env types.Envelope) error {
   206  	defer recoverLevelDBPanics("SaveEnvelope")
   207  
   208  	key := NewDBKey(env.Expiry()-env.TTL(), env.Topic(), env.Hash())
   209  	rawEnvelope, err := rlp.EncodeToBytes(env.Unwrap())
   210  	if err != nil {
   211  		log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err))
   212  		archivedErrorsCounter.WithLabelValues(db.name).Inc()
   213  		return err
   214  	}
   215  
   216  	if err = db.ldb.Put(key.Bytes(), rawEnvelope, nil); err != nil {
   217  		log.Error(fmt.Sprintf("Writing to DB failed: %s", err))
   218  		archivedErrorsCounter.WithLabelValues(db.name).Inc()
   219  	}
   220  	archivedEnvelopesGauge.WithLabelValues(db.name).Inc()
   221  	archivedEnvelopeSizeMeter.WithLabelValues(db.name).Observe(
   222  		float64(waku.EnvelopeHeaderLength + env.Size()))
   223  	return err
   224  }
   225  
   226  func (db *LevelDB) Close() error {
   227  	select {
   228  	case <-db.done:
   229  	default:
   230  		close(db.done)
   231  	}
   232  	return db.ldb.Close()
   233  }
   234  
   235  func recoverLevelDBPanics(calleMethodName string) {
   236  	// Recover from possible goleveldb panics
   237  	if r := recover(); r != nil {
   238  		if errString, ok := r.(string); ok {
   239  			log.Error(fmt.Sprintf("recovered from panic in %s: %s", calleMethodName, errString))
   240  		}
   241  	}
   242  }