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  }