github.com/prysmaticlabs/prysm@v1.4.4/slasher/db/kv/kv.go (about)

     1  // Package kv defines a bolt-db, key-value store implementation of
     2  // the slasher database interface.
     3  package kv
     4  
     5  import (
     6  	"context"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/prysmaticlabs/prysm/shared/fileutil"
    13  	"github.com/prysmaticlabs/prysm/shared/params"
    14  	"github.com/prysmaticlabs/prysm/slasher/cache"
    15  	bolt "go.etcd.io/bbolt"
    16  	"go.opencensus.io/trace"
    17  )
    18  
    19  const (
    20  	// SlasherDbDirName is the name of the directory containing the slasher database.
    21  	SlasherDbDirName = "slasherdata"
    22  	// DatabaseFileName is the name of the slasher database.
    23  	DatabaseFileName = "slasher.db"
    24  )
    25  
    26  // Store defines an implementation of the slasher Database interface
    27  // using BoltDB as the underlying persistent kv-store for Ethereum.
    28  type Store struct {
    29  	highestAttCacheEnabled  bool
    30  	spanCacheEnabled        bool
    31  	highestAttestationCache *cache.HighestAttestationCache
    32  	flatSpanCache           *cache.EpochFlatSpansCache
    33  	db                      *bolt.DB
    34  	databasePath            string
    35  }
    36  
    37  // Config options for the slasher db.
    38  type Config struct {
    39  	// SpanCacheSize determines the span map cache size.
    40  	SpanCacheSize               int
    41  	HighestAttestationCacheSize int
    42  }
    43  
    44  // Close closes the underlying boltdb database.
    45  func (s *Store) Close() error {
    46  	s.flatSpanCache.Purge()
    47  	s.highestAttestationCache.Purge()
    48  	return s.db.Close()
    49  }
    50  
    51  // RemoveOldestFromCache clears the oldest key out of the cache only if the cache is at max capacity.
    52  func (s *Store) RemoveOldestFromCache(ctx context.Context) uint64 {
    53  	ctx, span := trace.StartSpan(ctx, "slasherDB.removeOldestFromCache")
    54  	defer span.End()
    55  	epochRemoved := s.flatSpanCache.PruneOldest()
    56  	return epochRemoved
    57  }
    58  
    59  // ClearSpanCache clears the spans cache.
    60  func (s *Store) ClearSpanCache() {
    61  	s.flatSpanCache.Purge()
    62  }
    63  
    64  func (s *Store) update(fn func(*bolt.Tx) error) error {
    65  	return s.db.Update(fn)
    66  }
    67  func (s *Store) view(fn func(*bolt.Tx) error) error {
    68  	return s.db.View(fn)
    69  }
    70  
    71  // ClearDB removes any previously stored data at the configured data directory.
    72  func (s *Store) ClearDB() error {
    73  	if _, err := os.Stat(s.databasePath); os.IsNotExist(err) {
    74  		return nil
    75  	}
    76  	return os.Remove(filepath.Join(s.databasePath, DatabaseFileName))
    77  }
    78  
    79  // DatabasePath at which this database writes files.
    80  func (s *Store) DatabasePath() string {
    81  	return s.databasePath
    82  }
    83  
    84  func createBuckets(tx *bolt.Tx, buckets ...[]byte) error {
    85  	for _, bucket := range buckets {
    86  		if _, err := tx.CreateBucketIfNotExists(bucket); err != nil {
    87  			return err
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  // NewKVStore initializes a new boltDB key-value store at the directory
    94  // path specified, creates the kv-buckets based on the schema, and stores
    95  // an open connection db object as a property of the Store struct.
    96  func NewKVStore(dirPath string, cfg *Config) (*Store, error) {
    97  	hasDir, err := fileutil.HasDir(dirPath)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	if !hasDir {
   102  		if err := fileutil.MkdirAll(dirPath); err != nil {
   103  			return nil, err
   104  		}
   105  	}
   106  
   107  	datafile := path.Join(dirPath, DatabaseFileName)
   108  	boltDB, err := bolt.Open(datafile, params.BeaconIoConfig().ReadWritePermissions, &bolt.Options{Timeout: params.BeaconIoConfig().BoltTimeout})
   109  	if err != nil {
   110  		if errors.Is(err, bolt.ErrTimeout) {
   111  			return nil, errors.New("cannot obtain database lock, database may be in use by another process")
   112  		}
   113  		return nil, err
   114  	}
   115  	kv := &Store{db: boltDB, databasePath: dirPath}
   116  	kv.EnableSpanCache(true)
   117  	kv.EnableHighestAttestationCache(true)
   118  	flatSpanCache, err := cache.NewEpochFlatSpansCache(cfg.SpanCacheSize, persistFlatSpanMapsOnEviction(kv))
   119  	if err != nil {
   120  		return nil, errors.Wrap(err, "could not create new flat cache")
   121  	}
   122  	kv.flatSpanCache = flatSpanCache
   123  	highestAttCache, err := cache.NewHighestAttestationCache(cfg.HighestAttestationCacheSize, persistHighestAttestationCacheOnEviction(kv))
   124  	kv.highestAttestationCache = highestAttCache
   125  
   126  	if err := kv.db.Update(func(tx *bolt.Tx) error {
   127  		return createBuckets(
   128  			tx,
   129  			indexedAttestationsBucket,
   130  			indexedAttestationsRootsByTargetBucket,
   131  			historicIndexedAttestationsBucket,
   132  			historicBlockHeadersBucket,
   133  			compressedIdxAttsBucket,
   134  			validatorsPublicKeysBucket,
   135  			validatorsMinMaxSpanBucket,
   136  			validatorsMinMaxSpanBucketNew,
   137  			slashingBucket,
   138  			chainDataBucket,
   139  			highestAttestationBucket,
   140  		)
   141  	}); err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	return kv, err
   146  }
   147  
   148  // Size returns the db size in bytes.
   149  func (s *Store) Size() (int64, error) {
   150  	var size int64
   151  	err := s.db.View(func(tx *bolt.Tx) error {
   152  		size = tx.Size()
   153  		return nil
   154  	})
   155  	return size, err
   156  }