ava-labs / avalanchego

Go implementation of an Avalanche node.
https://avax.network
BSD 3-Clause "New" or "Revised" License
2.12k stars 668 forks source link

Add GetPreference to VM to enable telling the engine the VM's last preference on restart #2577

Open aaronbuchwald opened 9 months ago

aaronbuchwald commented 9 months ago

Context and scope This ticket intends to add a GetPreference function to the VM interface, so that if the node shuts down while items are in consensus, the VM can tell the consensus engine what it's last preference was on restart.

The consensus engine will use the new function to grab the last preference of the VM, recurse through the blocks to the last accepted block, and sequentially insert the processing blocks up to the last preference on startup.

Interface Changes

The proposed change will add GetPreference to the ChainVM interface

GetPreference(ctx context.Context) (ids.ID, error)

this will mirror SetPreference here: https://github.com/ava-labs/avalanchego/blob/7623ffd4be915a5185c9ed5e11fa9be15a6e1f00/snow/engine/snowman/block/vm.go#L50.

The VM can determine how to implement GetPreference so that the initial implementation can be purely in-memory serving the last accepted blockID as the last preference. The recommended behavior will be to persist the preference to disk, so that in the event of an unexepcted shutdown, the consensus preference is kept the same after a restart.

To support that behavior in the case of an unexpected shutdown robustly, the node/VM will need to consider whether or not writing the preference to disk via the database is sufficient (which will be database dependent). For example, if a database does not provide durability after a put operation completes, then the node/VM may want to take extra care to ensure the preference is persisted by manually flushing in-memory buffers either after writing the preference or during shutdown.

Planned Changes

aaronbuchwald commented 9 months ago

Note: AvalancheGo uses LevelDB by default, which guarantees durability after a process failure, but not a machine failure according to its documentation here: https://github.com/syndtr/goleveldb/blob/126854af5e6d8295ef8e8bee3040dd8380ae72e8/leveldb/opt/options.go#L731

aaronbuchwald commented 9 months ago

We could alternatively implement this in one place in either the consensus engine or ProposerVM

aaronbuchwald commented 8 months ago

Discussed with @StephenButtolph and decided on an implementation path here:

  1. Separate this out to be implemented by the VM (up to the VM to implement this in memory or actually persist to disk)
  2. Implement it in the ProposerVM (including storing the inner VM's blocks), which will ensure this is handled correctly for every VM
  3. Implement it in the EVM chains (Subnet-EVM and Coreth) since it should be trivial to implement there)
  4. Implement the new interface in memory for the remaining VMs (ProposerVM wrapper ensures the preference is written to disk for these VMs, so they do not require any further changes)

To implement this in the ProposerVM, we will need to ensure that any block that is preferred has been persisted to disk. To avoid writing blocks to disk repeatedly when the engine switches its preference, this will require making a few changes to how the ProposerVM persists blocks:

github-actions[bot] commented 6 months ago

This issue has become stale because it has been open 60 days with no activity. Adding the lifecycle/frozen label will cause this issue to ignore lifecycle events.

joshua-kim commented 6 months ago

Related: https://github.com/ava-labs/avalanchego/pull/2639

github-actions[bot] commented 4 months ago

This issue has become stale because it has been open 60 days with no activity. Adding the lifecycle/frozen label will cause this issue to ignore lifecycle events.

github-actions[bot] commented 1 month ago

This issue has become stale because it has been open 60 days with no activity. Adding the lifecycle/frozen label will cause this issue to ignore lifecycle events.

joshua-kim commented 1 month ago

Marking as frozen.