Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize chain selection in ChainDB #1223

Closed
edsko opened this issue Nov 13, 2019 · 3 comments · Fixed by #2323
Closed

Optimize chain selection in ChainDB #1223

edsko opened this issue Nov 13, 2019 · 3 comments · Fixed by #2323
Labels
chain db consensus issues related to ouroboros-consensus optimisation Performance optimisation

Comments

@edsko
Copy link
Contributor

edsko commented Nov 13, 2019

The chain selection code in the chain DB follows the specification very closely, but of course the specification is not concerned with performance, only efficiency. Chain selection works by finding a set of candidates starting at a particular point; if a new block comes in, and that block fits onto our current chain, the set of candidates is computed from current tip of the chain. However, if that block does not find onto our chain, we construct a prefix path backwards from that block to the tip of the immutable DB, find all candidates starting at the new block, and then construct the set of candidates by prepending that prefix path to those candidates.

imm DB tip I-------|-------------------------- our chain
                   |
                   --------------B new header

imm DB tip I=======X-------------------------- our chain
                   |
                   |   rollback
                   ===============B----------
                                  |
                                  *-------------------
                                  |
                                  *--------------                   

We then construct a set of "candidate suffixes", by computing an intersection of that chain with our current chain, and then using that to compute a rollback (in terms of a number of blocks) and a set of new headers. However, of course, the rollback will in fact be the same for all of those candidates, and there was no need to recompute the intersection point at all, because we knew that as soon as we computed the prefix back from the new header to our current chain.

This means that the implementation is correct, but is doing unnecessary work, which will become important once chains are more frequent. Therefore marking this as high priority, but only for shelley.

@edsko edsko added consensus issues related to ouroboros-consensus priority high optimisation Performance optimisation labels Nov 13, 2019
@edsko
Copy link
Contributor Author

edsko commented Dec 18, 2019

Dropping priority to medium, since we could release without this: the amount of extra work is not huge. It would however be good to do. Certainly not required for a testnet.

@edsko
Copy link
Contributor Author

edsko commented Apr 29, 2020

Note: if we trigger chain selection for a block already on the current chain, this optimization should imply that the path we find is the empty path.

mrBliss added a commit that referenced this issue Jun 25, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a reverse path through the VolatileDB. By using these paths
  everywhere, we no longer have to think about looking things up in the
  VolatileDB.

* `VolDB`: let `isReachable` return a `PointChainDiff` instead of a list of
  hashes going all the way back to the immutable tip. This `PointChainDiff`
  can later be translated to a `ChainDiff`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* `Fragment.Diff`: promote the internal `mkRollback` to the public `mkDiff`
  smart constructor, now used by chain selection for forks.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 25, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a reverse path through the VolatileDB. By using these paths
  everywhere, we no longer have to think about looking things up in the
  VolatileDB.

* `VolDB`: let `isReachable` return a `PointChainDiff` instead of a list of
  hashes going all the way back to the immutable tip. This `PointChainDiff`
  can later be translated to a `ChainDiff`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* `Fragment.Diff`: promote the internal `mkRollback` to the public `mkDiff`
  smart constructor, now used by chain selection for forks.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 26, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `VolatilePath` and `computeVolatilePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `PointChainDiff` instead of a list of
  hashes going all the way back to the immutable tip. This `PointChainDiff`
  can later be translated to a `ChainDiff`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeVolatilePath`.

* `Fragment.Diff`: promote the internal `mkRollback` to the public `new` smart
  constructor, now used by chain selection for forks.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 26, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `VolatilePath` and `computeVolatilePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `PointChainDiff` instead of a list of
  hashes going all the way back to the immutable tip. This `PointChainDiff`
  can later be translated to a `ChainDiff`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeVolatilePath`.

* `Fragment.Diff`: promote the internal `mkRollback` to the public `new` smart
  constructor, now used by chain selection for forks.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 26, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `VolatilePath` and `computeVolatilePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `PointChainDiff` instead of a list of
  hashes going all the way back to the immutable tip. This `PointChainDiff`
  can later be translated to a `ChainDiff`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeVolatilePath`.

* `Fragment.Diff`: promote the internal `mkRollback` to the public `new` smart
  constructor, now used by chain selection for forks.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 29, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `ChainDiff` of `HeaderFields` instead of
  a list of hashes going all the way back to the immutable tip. This
  `ChainDiff` of `HeaderFields` can later be translated to a `ChainDiff` of
  `Header`s.

* `VolDB`: add `extendWithSuccessors` that will extend a `ChainDiff` of
  `HeaderFields` with its successors, using `candidates`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 29, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `ChainDiff` of `HeaderFields` instead of
  a list of hashes going all the way back to the immutable tip. This
  `ChainDiff` of `HeaderFields` can later be translated to a `ChainDiff` of
  `Header`s.

* `VolDB`: add `extendWithSuccessors` that will extend a `ChainDiff` of
  `HeaderFields` with its successors, using `candidates`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
mrBliss added a commit that referenced this issue Jun 29, 2020
Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `ChainDiff` of `HeaderFields` instead of
  a list of hashes going all the way back to the immutable tip. This
  `ChainDiff` of `HeaderFields` can later be translated to a `ChainDiff` of
  `Header`s.

* `VolDB`: add `extendWithSuccessors` that will extend a `ChainDiff` of
  `HeaderFields` with its successors, using `candidates`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.
iohk-bors bot added a commit that referenced this issue Jun 29, 2020
2323: ChainDB: optimise chain selection for forks r=mrBliss a=mrBliss

Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `ChainDiff` of `HeaderFields` instead of
  a list of hashes going all the way back to the immutable tip. This
  `ChainDiff` of `HeaderFields` can later be translated to a `ChainDiff` of
  `Header`s.

* `VolDB`: add `extendWithSuccessors` that will extend a `ChainDiff` of
  `HeaderFields` with its successors, using `candidates`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.

Co-authored-by: Thomas Winant <[email protected]>
iohk-bors bot added a commit that referenced this issue Jun 30, 2020
2323: ChainDB: optimise chain selection for forks r=mrBliss a=mrBliss

Fixes #1223.

When a block fits onto a fork, we construct the fragment all the way going
back to the immutable tip instead of just a `ChainDiff` to the most recent
intersection with the current chain. For example, when switching to a fork
that requires rolling back 2 blocks, previously, we'd construct a fragment
containing `k` headers. Now, we'll construct a `ChainDiff` with rollback = 2
and a fragment of 2 headers.

* `VolDB`: introduce `ReversePath` and `computeReversePath` which lazily
  computes a path through the VolatileDB. By using these paths everywhere, we
  no longer have to think about looking things up in the VolatileDB.

* `VolDB`: let `isReachable` return a `ChainDiff` of `HeaderFields` instead of
  a list of hashes going all the way back to the immutable tip. This
  `ChainDiff` of `HeaderFields` can later be translated to a `ChainDiff` of
  `Header`s.

* `VolDB`: add `extendWithSuccessors` that will extend a `ChainDiff` of
  `HeaderFields` with its successors, using `candidates`.

* Add property tests for `isReachable`, which helped catch a tricky corner
  case involving EBBs.

* `VolDB`: rewrite `computePath` using `computeReversePath`.

* We can now use `RealPoint`s instead of `HeaderHash`s in many functions and
  types: `IteratorBlockGCed`, `ChainDB.Iterator`s, `VolDB.Path`, some trace
  events, etc.

We don't yet take advantage of the `RealPoint` in the implementation of
`ChainDB.Iterator`s.

Co-authored-by: Thomas Winant <[email protected]>
@iohk-bors iohk-bors bot closed this as completed in e71bcf7 Jun 30, 2020
@mrBliss mrBliss added this to the S16 2020-07-02 milestone Jun 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chain db consensus issues related to ouroboros-consensus optimisation Performance optimisation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants