github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/db/kv/backup.go (about) 1 package kv 2 3 import ( 4 "context" 5 "fmt" 6 "path" 7 8 "github.com/pkg/errors" 9 "github.com/prysmaticlabs/prysm/shared/fileutil" 10 "github.com/prysmaticlabs/prysm/shared/params" 11 bolt "go.etcd.io/bbolt" 12 "go.opencensus.io/trace" 13 ) 14 15 const backupsDirectoryName = "backups" 16 17 // Backup the database to the datadir backup directory. 18 // Example for backup at slot 345: $DATADIR/backups/prysm_beacondb_at_slot_0000345.backup 19 func (s *Store) Backup(ctx context.Context, outputDir string, permissionOverride bool) error { 20 ctx, span := trace.StartSpan(ctx, "BeaconDB.Backup") 21 defer span.End() 22 23 var backupsDir string 24 var err error 25 if outputDir != "" { 26 backupsDir, err = fileutil.ExpandPath(outputDir) 27 if err != nil { 28 return err 29 } 30 } else { 31 backupsDir = path.Join(s.databasePath, backupsDirectoryName) 32 } 33 head, err := s.HeadBlock(ctx) 34 if err != nil { 35 return err 36 } 37 if head == nil || head.IsNil() { 38 return errors.New("no head block") 39 } 40 // Ensure the backups directory exists. 41 if err := fileutil.HandleBackupDir(backupsDir, permissionOverride); err != nil { 42 return err 43 } 44 backupPath := path.Join(backupsDir, fmt.Sprintf("prysm_beacondb_at_slot_%07d.backup", head.Block().Slot())) 45 log.WithField("backup", backupPath).Info("Writing backup database.") 46 47 copyDB, err := bolt.Open( 48 backupPath, 49 params.BeaconIoConfig().ReadWritePermissions, 50 &bolt.Options{NoSync: true, Timeout: params.BeaconIoConfig().BoltTimeout, FreelistType: bolt.FreelistMapType}, 51 ) 52 if err != nil { 53 return err 54 } 55 copyDB.AllocSize = boltAllocSize 56 57 defer func() { 58 if err := copyDB.Close(); err != nil { 59 log.WithError(err).Error("Failed to close backup database") 60 } 61 }() 62 // Prefetch all keys of buckets, and inner keys in a 63 // bucket to use less memory usage when backing up. 64 var bucketKeys [][]byte 65 bucketMap := make(map[string][][]byte) 66 err = s.db.View(func(tx *bolt.Tx) error { 67 return tx.ForEach(func(name []byte, b *bolt.Bucket) error { 68 newName := make([]byte, len(name)) 69 copy(newName, name) 70 bucketKeys = append(bucketKeys, newName) 71 var innerKeys [][]byte 72 err := b.ForEach(func(k, v []byte) error { 73 if k == nil { 74 return nil 75 } 76 nKey := make([]byte, len(k)) 77 copy(nKey, k) 78 innerKeys = append(innerKeys, nKey) 79 return nil 80 }) 81 if err != nil { 82 return err 83 } 84 bucketMap[string(newName)] = innerKeys 85 return nil 86 }) 87 }) 88 if err != nil { 89 return err 90 } 91 // Utilize much smaller writes, compared to 92 // writing for a whole bucket in a single transaction. Also 93 // prevent long-running read transactions, as Bolt doesn't 94 // handle those well. 95 for _, k := range bucketKeys { 96 log.Debugf("Copying bucket %s\n", k) 97 innerKeys := bucketMap[string(k)] 98 for _, ik := range innerKeys { 99 err = s.db.View(func(tx *bolt.Tx) error { 100 bkt := tx.Bucket(k) 101 return copyDB.Update(func(tx2 *bolt.Tx) error { 102 b2, err := tx2.CreateBucketIfNotExists(k) 103 if err != nil { 104 return err 105 } 106 return b2.Put(ik, bkt.Get(ik)) 107 }) 108 }) 109 if err != nil { 110 return err 111 } 112 } 113 } 114 // Re-enable sync to allow bolt to fsync 115 // again. 116 copyDB.NoSync = false 117 return nil 118 }