Repairing Nodes¶
Overview¶
The Canton platform is generally built to self-heal and automatically recover from issues. As such, if there is a situation where some degradation can be expected, there should be some code that yields graceful degradation and automated recovery from said issues.
Common examples are database outages (retry until success) or network outages (failover and reconnect until success).
Canton should report such issues as warnings to alert an operator about the degradation of its dependencies, but generally, should not require any manual intervention to recover from a degradation.
However, not all situations can be foreseen and corruptions of systems can always happen in unanticipated ways. Therefore, Canton can always be manually repaired somehow. This means that whatever the corruption is, there are a series of operational steps that can be made in order to recover the correct state of a node. If several nodes in the distributed system are affected, it may be necessary to coordinate the recovery among the affected nodes.
Conceptually, this means that Canton recovery is structured along the four layers:
- Automated self-recovery and self-healing.
- Recovery from crash or restart by re-creating a consistent state from the persisted store.
- Standard disaster recovery from a database backup in case of database outage and replay from domain.
- Corruption disaster recovery using repair and other console commands to re-establish a consistent state within the distributed system.
If you run into corruption issues, you need to first understand what caused the issue. Ideally, you can contact our support team to help you diagnose the issue and provide you with a custom recipe on how to recover from your issue (and prevent recurrence).
The toolbox the support engineers have at hand are:
- Exporting / importing secret keys
- Manually initializing nodes
- Exporting / importing DARs
- Exporting / importing topology transactions
- Manually adding or removing contracts from the active contract set
- Moving contracts from one domain to another
- Manually ignoring faulty transactions (and then using add / remove contract to repair the ACS).
All these methods are very powerful but dangerous. You should not attempt to repair your nodes on your own as you risk severe data corruption.
Keep in mind that the corruption of the system state may not have been discovered immediately; thus, the corruption may have leaked out through the APIs to the applications using the corrupted node. Bringing the node back into a correct state with respect to the other nodes in the distributed system can therefore make the application state inconsistent with the nodes state. Accordingly, the application should either re-initialize itself from the repaired state or itself offer tools to fix inconsistencies.
Repair Macros¶
Some operations are combined as macros, which are a series of consecutive repair commands, coded as a single command. While we discourage you from using these commands on your own, we document them here for the sake of completeness. These macros are available only in the enterprise edition.
Clone Identity¶
Many nodes can be rehydrated from a domain, as long as the domain is not pruned. In such situations, you might want to reset your node while keeping the identity and the secret keys of the node. This can be done using the repair macros.
You need local console access to the node. If you are running your production node in a container, you need to create a new configuration file that allows you to access the database of the node from an interactive console. Make sure that the normal node process is stopped and that nothing else is accessing the same database (e.g. ensure that replication is turned on). Also, make sure that the nodes are configured to not perform auto-initialization, as this would create a new identity. You ensure that by setting the corresponding auto-init configuration option to false:
canton.participants.myparticipant.init.auto-init = false
Then start Canton interactively using:
./bin/canton -c myconfig --manual-start
Starting with --manual-start
will prevent the participant to attempt to reconnect to the domains.
Then, you can download the identity state of the node to a directory on the machine you are running the
process:
repair.identity.download(participant, tempDirParticipant)
repair.dars.download(participant, tempDirParticipant)
participant.stop()
This will store the secret keys, the topology state and the identity onto the disk in the given directory.
You can run the identity.download
command on all nodes. However, mediator and sequencer
nodes will only store their keys in files, as the sequencer’s identity is attached to the domain identity
and the mediator’s identity is set only later during initialization.
The dars.download
command is a convenience command to download all dars that have been
added to the participant via the console command participant.dars.upload
. Dars that
were uploaded through the Ledger API need to be manually re-uploaded to the new participant.
Once the data is stored, stop the node and then truncate the database (please back it up before). Then restart the node and upload the identity data again:
participant.start()
repair.identity.upload(participant, tempDirParticipant)
repair.dars.upload(participant, tempDirParticipant)
Please note that dar uploading is only necessary for participants.
Now, depending on the node type, you need to re-integrate the node into the domain. For the domain nodes, you need to grab the static domain parameters and the domain id from the domain manager. If you have remote access to the domain manager, you can run
val domainId = domainManager1.id
val domainParameters = domainManager1.service.get_static_domain_parameters
You also want to grab the mediator identities for each mediator using:
val mediatorId = mediator.id
For the sequencer, rehydration works only if the domain uses a blockchain; the database-only sequencers cannot rehydrate. So rehydration for blockchain-based sequencers will be:
repair.identity.upload(newSequencer, tempDirSequencer)
newSequencer.initialization.initialize_from_beginning(domainId, domainParameters)
newSequencer.health.wait_for_initialized()
For the domain manager, it looks like:
repair.identity.upload(domainManager2, tempDirDomainManager)
domainManager2.setup.init(newSequencer)
domainManager2.health.wait_for_initialized()
For the mediator, it would be:
repair.identity.upload(mediator, tempDirMediator)
mediator.mediator.initialize(
domainId,
mediatorId,
domainParameters,
newSequencer,
topologySnapshot = None,
)
mediator.health.wait_for_initialized()
For a participant, you would reconnect it to the domain using a normal connect:
participant.domains.connect_local(sequencer)
Note that this will replay all transactions from the domain. However, command deduplication will only be fully functional once the participant catches up with the domain. Therefore, you need to ensure that applications relying on command deduplication do not submit commands during recovery.