github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/snapshot.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"fmt"
    13  	"hash"
    14  	"io/ioutil"
    15  	"os"
    16  	"path/filepath"
    17  
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  const (
    22  	snapshotMetadataFileName     = "_snapshot_signable_metadata.json"
    23  	snapshotMetadataHashFileName = "_snapshot_additional_info.json"
    24  	jsonFileIndent               = "    "
    25  )
    26  
    27  // snapshotSignableMetadata is used to build a JSON that represents a unique snapshot and
    28  // can be signed by the peer. Hashsum of the resultant JSON is intended to be used as a single
    29  // hash of the snapshot, if need be.
    30  type snapshotSignableMetadata struct {
    31  	ChannelName        string            `json:"channel_name"`
    32  	ChannelHeight      uint64            `json:"channel_height"`
    33  	LastBlockHashInHex string            `json:"last_block_hash"`
    34  	FilesAndHashes     map[string]string `json:"snapshot_files_raw_hashes"`
    35  }
    36  
    37  type snapshotAdditionalInfo struct {
    38  	SnapshotHashInHex        string `json:"snapshot_hash"`
    39  	LastBlockCommitHashInHex string `json:"last_block_commit_hash"`
    40  }
    41  
    42  // generateSnapshot generates a snapshot. This function should be invoked when commit on the kvledger are paused
    43  // after committing the last block fully and further the commits should not be resumed till this function finishes
    44  func (l *kvLedger) generateSnapshot() error {
    45  	snapshotsRootDir := l.snapshotsConfig.RootDir
    46  	bcInfo, err := l.GetBlockchainInfo()
    47  	if err != nil {
    48  		return err
    49  	}
    50  	snapshotTempDir, err := ioutil.TempDir(
    51  		InProgressSnapshotsPath(snapshotsRootDir),
    52  		fmt.Sprintf("%s-%d-", l.ledgerID, bcInfo.Height),
    53  	)
    54  	if err != nil {
    55  		return errors.Wrapf(err, "error while creating temp dir [%s]", snapshotTempDir)
    56  	}
    57  	newHashFunc := func() (hash.Hash, error) {
    58  		return l.hashProvider.GetHash(snapshotHashOpts)
    59  	}
    60  	txIDsExportSummary, err := l.blockStore.ExportTxIds(snapshotTempDir, newHashFunc)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	configsHistoryExportSummary, err := l.configHistoryRetriever.ExportConfigHistory(snapshotTempDir, newHashFunc)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	stateDBExportSummary, err := l.txmgr.ExportPubStateAndPvtStateHashes(snapshotTempDir, newHashFunc)
    69  	if err != nil {
    70  		return err
    71  	}
    72  
    73  	if err := l.generateSnapshotMetadataFiles(
    74  		snapshotTempDir, txIDsExportSummary,
    75  		configsHistoryExportSummary, stateDBExportSummary,
    76  	); err != nil {
    77  		return err
    78  	}
    79  	if err := syncDir(snapshotTempDir); err != nil {
    80  		return err
    81  	}
    82  	slgr := SnapshotsDirForLedger(snapshotsRootDir, l.ledgerID)
    83  	if err := os.MkdirAll(slgr, 0755); err != nil {
    84  		return errors.Wrapf(err, "error while creating final dir for snapshot:%s", slgr)
    85  	}
    86  	if err := syncParentDir(slgr); err != nil {
    87  		return err
    88  	}
    89  	slgrht := SnapshotDirForLedgerHeight(l.snapshotsConfig.RootDir, l.ledgerID, bcInfo.Height)
    90  	if err := os.Rename(snapshotTempDir, slgrht); err != nil {
    91  		return errors.Wrapf(err, "error while renaming dir [%s] to [%s]:", snapshotTempDir, slgrht)
    92  	}
    93  	return syncParentDir(slgrht)
    94  }
    95  
    96  func (l *kvLedger) generateSnapshotMetadataFiles(
    97  	dir string,
    98  	txIDsExportSummary,
    99  	configsHistoryExportSummary,
   100  	stateDBExportSummary map[string][]byte) error {
   101  	// generate metadata file
   102  	filesAndHashes := map[string]string{}
   103  	for fileName, hashsum := range txIDsExportSummary {
   104  		filesAndHashes[fileName] = hex.EncodeToString(hashsum)
   105  	}
   106  	for fileName, hashsum := range configsHistoryExportSummary {
   107  		filesAndHashes[fileName] = hex.EncodeToString(hashsum)
   108  	}
   109  	for fileName, hashsum := range stateDBExportSummary {
   110  		filesAndHashes[fileName] = hex.EncodeToString(hashsum)
   111  	}
   112  	bcInfo, err := l.GetBlockchainInfo()
   113  	if err != nil {
   114  		return err
   115  	}
   116  	metadata, err := json.MarshalIndent(
   117  		&snapshotSignableMetadata{
   118  			ChannelName:        l.ledgerID,
   119  			ChannelHeight:      bcInfo.Height,
   120  			LastBlockHashInHex: hex.EncodeToString(bcInfo.CurrentBlockHash),
   121  			FilesAndHashes:     filesAndHashes,
   122  		},
   123  		"",
   124  		jsonFileIndent,
   125  	)
   126  	if err != nil {
   127  		return errors.Wrap(err, "error while marshelling snapshot metadata to JSON")
   128  	}
   129  	if err := createAndSyncFile(filepath.Join(dir, snapshotMetadataFileName), metadata); err != nil {
   130  		return err
   131  	}
   132  
   133  	// generate metadata hash file
   134  	hash, err := l.hashProvider.GetHash(snapshotHashOpts)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	if _, err := hash.Write(metadata); err != nil {
   139  		return err
   140  	}
   141  	metadataAdditionalInfo, err := json.MarshalIndent(
   142  		&snapshotAdditionalInfo{
   143  			SnapshotHashInHex:        hex.EncodeToString(hash.Sum(nil)),
   144  			LastBlockCommitHashInHex: hex.EncodeToString(l.commitHash),
   145  		},
   146  		"",
   147  		jsonFileIndent,
   148  	)
   149  	if err != nil {
   150  		return errors.Wrap(err, "error while marshalling snapshot additional info to JSON")
   151  	}
   152  	return createAndSyncFile(filepath.Join(dir, snapshotMetadataHashFileName), metadataAdditionalInfo)
   153  }
   154  
   155  func createAndSyncFile(filePath string, content []byte) error {
   156  	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0444)
   157  	if err != nil {
   158  		return errors.Wrapf(err, "error while creating file:%s", filePath)
   159  	}
   160  	_, err = file.Write(content)
   161  	if err != nil {
   162  		file.Close()
   163  		return errors.Wrapf(err, "error while writing to file:%s", filePath)
   164  	}
   165  	if err = file.Sync(); err != nil {
   166  		file.Close()
   167  		return errors.Wrapf(err, "error while synching the file:%s", filePath)
   168  	}
   169  	if err := file.Close(); err != nil {
   170  		return errors.Wrapf(err, "error while closing the file:%s", filePath)
   171  	}
   172  	return nil
   173  }
   174  
   175  func syncParentDir(path string) error {
   176  	return syncDir(filepath.Dir(path))
   177  }
   178  
   179  func syncDir(dirPath string) error {
   180  	dir, err := os.Open(dirPath)
   181  	if err != nil {
   182  		return errors.Wrapf(err, "error while opening dir:%s", dirPath)
   183  	}
   184  	if err := dir.Sync(); err != nil {
   185  		dir.Close()
   186  		return errors.Wrapf(err, "error while synching dir:%s", dirPath)
   187  	}
   188  	if err := dir.Close(); err != nil {
   189  		return errors.Wrapf(err, "error while closing dir:%s", dirPath)
   190  	}
   191  	return err
   192  }