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 }