Ledger Snapshot
新加入节点可以不用从0号区块开始同步区块,而是直接同步通道的snapshot信息,从而达到快速加入通道的作用。
snapshot的优点
- 节点可以不用从初识区块开始处理全部的区块:节点不用处理通道的历史区块就可以加入到一个通道里,减少了节点加入到已有通道的时间。
- 节点可以使用通道的最新配置加入通道:snapshot中包括了通道的最新配置,节点可以直接利用这些最新配置加入到通道中。这一点对于修改了orderer节点的TLS证书这样的通道更新尤为的重要。
- 减少存储消耗:新节点通过snapshot加入到通道之后不需要再同步历史区块,减少了存储消耗。
- 状态检查点:节点的管理者可以将当前的通道状态进行snapshot并和其他节点(同组织的或者不同组织的)的snapshot进行比较,用以做一致性和完整性的校验。
snapshot的缺点
- snapshot之前的历史区块及历史区块中的交易无法查询。同理也无法查询snapshot之前的关于一个key的历史记录。例如一个peer使用snapshot加入通道,snapshot是在区块高度为1000时创建的,那么从0号区块-999区块都不可以查询。有需要进行相关查询的时候,应用需要找到拥有这些历史区块的节点。因此组织最好保留至少一个节点的历史数据,以供历史数据的查询。
- 节点在某个高度进行snapshot期间,不会提交区块,但是会正常进行背书和查询。因为进行snapshot是一项资源密集型(resource-intensive)操作,节点在处理背书交易或者向其他通道提交区块时也可能会变得缓慢。由于这些原因,最好只在需要的时候进行snapshot操作。例如一个新的节点要加入到通道中,或者组织想要验证账本有没有分叉。
- 私密数据不包含在snapshot中(私密数据的hash包含在snapshot中,但数据本身不在)。节点加入使用snapshot加入到通道将会从拥有私密数据的节点拉取私密数据信息,这可能需要一定的时间。
- 对于通过snapshot加入到通道中的peer,不能使用
reset
,rollback
,rebuild-dbs
等命令,因为该节点并没有完整的区块信息。这些节点通过snapshot中的信息来加入通道并重建数据。
一些思考
对于节点加入通道,目前支持两种模式,即原有的通过创世块加入和新增的通过snapshot来加入。
- 通过创世块加入,然后从排序节点或者其他记账节点拉取区块,并在本地进行处理,直到最新的区块。这种场景下,世界状态是通过区块创建的。
- 通过snapshot加入,其中包括了通道账本在特定高度的最小集数据。不需要从其他节点拉取和处理区块,世界状态从snapshot中恢复。
对于如何选择是通过创世块加入,还是通过snapshot加入
- 时间维度,衡量通过创世块加入所需的时间。如果历史区块非常多,时间很久,考虑通过snapshot加入。
- orderer节点影响,衡量是否需要从通道的最原始配置的排序节点拉取区块(这些节点可能在后来更新中不再存在或者替换了tls证书)
- 历史查询维度,如果需要查询历史的区块、交易和世界状态,则只能通过创世块加入。
流程介绍
生成snapshot
安排snapshot,节点在同一高度进行snapshot(这样才可以进行一些校验),该高度必须等于或者大于当前高度。
-
当高度到达设定后,节点生成snapshot。snapshot包括
公开的世界状态(PublicState),包括所有key的最新value。(不包括历史记录)
私密数据的Hash
交易IDs,主要用于之后做交易ID重复性检查用。
Collection配置历史,包括所有链码的私密数据配置的历史记录集合。
-
_snapshot_signable_metadata.json包含
- channel_name: 通道名称
- last_block_number: snapshot对应的区块高度
- last_block_hash: snapshot对应的区块hash
- previous_block_hash:前一个区块的hash
- state_db_type:世界状态数据库类型CouchDB或者SimpleKeyValueDB(leveldb)
- snapshot_files_raw_hashes,记录的其它文件对应的hash
{ "channel_name": "channel1", "last_block_number": 3055, "last_block_hash": "1f7810f2372e74c8f35b6d6229bc66cf93e5f7117f5b2720bc0d5c66323057f8", "previous_block_hash": "fcd6437725e73eb359f9f3547a0d11d7bfab7ee99ea2e93d052500e0c4c4cebd", "snapshot_files_raw_hashes": { "private_state_hashes.data": "01ba011132791d18fd73bc24629bf90122713dbd4a6be2e4412067688bd20f06", "private_state_hashes.metadata": "053b031f9bb556bae6cec2e7692afdffc8537a9c1a4d0c7f1584fd18deedd4f9", "public_state.data": "2f00341f8bc7fe7f7bf40d1873e81530496c93bd58280f7b47abc2cc9be6fd0a", "public_state.metadata": "fc6de3a2a90fc1fa83b8abeff26fd363d99c33e6e2013256b82be220bec97143", "txids.data": "6970e911152d023b3b8ed29c80bf7a284d7a620fe4d457b8d3f0f23c4aca1a4b", "txids.metadata": "60eca34567c4b2ab62d0c934a74304c44994ec63c9b2265c34432ae7b8ad4452" }, "state_db_type": "SimpleKeyValueDB" }
-
_snapshot_additional_metadata.json包含
- snapshot_hash,文件_snapshot_signable_metadata.json的hash。
- last_block_commit_hash,
{ "snapshot_hash": "272a05ee90c6dec1e0098399e292f1e1b2905e76e99107ca2992d8a6f90bc6e0", "last_block_commit_hash": "4ccda36b2b14edac910a09eac8e8a07b6b6bcc45d6c2139766ddf7df38628df1" }
通过snapshot加入通道
通过线下方式获得snapshot文件
验证snapshot或者snapshots,peer节点组织的管理员可以通过使用snapshot让peer加入到通道内,加入之前会计算snapshot文件和hash是否一致。另外管理员也可以通过可信从多个组织和节点获取snapshots,也可以要求组织对metadata文件进行签名。
peer使用snapshot加入到通道。当节点完成加入之后,会拉取私密数据,然后开始正常提交区块。
实际操作
生成snapshot,使用peer snapshot submitrequest命令来生成snapshot。
peer snapshot submitrequest -c <name of channel> -b <ledger height where snapshot will be taken> --peerAddress <address of peer> --tlsRootCertFile <path to root certificate of the TLS CA>
例如
peer snapshot submitrequest -c testchannel -b 1000 --peerAddress 127.0.0.1:22509 --tlsRootCertFile tls/cert.pem
如果给定的高度为0,即代表在当前高度进行snapshot。
可以通过peer snapshot listpending查询是否有待执行的snapshot操作
peer snapshot listpending -c testchannel --peerAddress 127.0.0.1:22509 --tlsRootCertFile tls/cert.pem
可以通过peer snapshot cancelrequest删除待执行的snapshot操作
peer snapshot cancelrequest -c testchannel -b 1000 --peerAddress 127.0.0.1:22509 --tlsRootCertFile tls/cert.pem
通过snapshot加入通道
首先将snapshot文件放入到peer所运行的环境中(比方说peer容器内部)
通过命令加入通道
peer channel joinbysnapshot --snapshotpath <path to snapshot>
- 查询是否加入成功
peer channel getinfo -c <name of channel joined by snapshot>