-
Notifications
You must be signed in to change notification settings - Fork 111
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
Fix poll_ready usage in ChainVerifier #1700
Conversation
And rename the chain verifier fields so `block` means `Arc<Block>`, and `block_verifier` means `Buffer<BlockVerifier, ...>`.
/// block could not be checkpointed | ||
Checkpoint(#[source] VerifyCheckpointError), | ||
Checkpoint(#[source] BoxError), | ||
/// block could not be verified | ||
Block(#[source] VerifyBlockError), | ||
Block(#[source] BoxError), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're using a Buffer
, we get BoxError
s back from the checkpoint and block verifiers.
89dd361
to
1c8c435
Compare
// We can't call `poll_ready` on the block and checkpoint verifiers here, | ||
// because each `poll_ready` must be followed by a `call`, and we don't | ||
// know which verifier we're going to choose yet. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't call poll_ready
here - it doesn't matter what we do with the results, it's the call that takes up a buffer slot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yaahc - this constraint is something to keep in mind for future service code reviews.
If we call poll_ready
, but there isn't a matching call
, we fill up our Buffer
s with unused reservations, and Zebra hangs.
.checkpoint | ||
.call(block) | ||
.checkpoint_verifier | ||
.clone() | ||
.oneshot(block) | ||
.map_err(VerifyChainError::Checkpoint) | ||
.boxed(), | ||
// This also covers blocks with no height, which the block verifier | ||
// will reject immediately. | ||
_ => self | ||
.block | ||
.call(block) | ||
.block_verifier | ||
.clone() | ||
.oneshot(block) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These services are now Buffer
ed, so we don't need an async
block. We can just call ServiceExt::oneshot
on a cloned service, and return those futures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yaahc @oxarbitrage the best way to avoid poll_ready
/ call
mismatches is to just use a oneshot
.
It handles the poll_ready
and call
internally, so we can't possibly get it wrong.
let block_verifier = BlockVerifier::new(network, state_service.clone()); | ||
let checkpoint_verifier = CheckpointVerifier::from_checkpoint_list(list, tip, state_service); | ||
|
||
let block_verifier = Buffer::new(block_verifier, VERIFIER_BUFFER_BOUND); | ||
let checkpoint_verifier = Buffer::new(checkpoint_verifier, VERIFIER_BUFFER_BOUND); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit easier to read the code if we create the services, then replace them with buffered versions.
And: * make the code look like the `main` branch as much as possible * document the `poll_ready`/`call` invariant
// Correctness: | ||
// | ||
// We use `ServiceExt::oneshot` to make sure every `poll_ready` has a | ||
// matching `call`. See #1593 for details. | ||
let tip = match state_service | ||
.ready_and() | ||
.await | ||
.unwrap() | ||
.call(zs::Request::Tip) | ||
.clone() | ||
.oneshot(zs::Request::Tip) | ||
.await | ||
.unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fix is part of #1675 - and it's actually something I missed during my review last week.
Since we're fixing this file, let's also use ServiceExt::oneshot
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this PR is fine now, but I wrote some of it, so @oxarbitrage or @yaahc should review it before it merges.
@teor2345 this looks great and thank you for all the comments, the logic looks good and the code very clean and concise. I had a few problems with the references we had before to do this change. 1- #1593 was too long and general. This is ok and all useful information but i got lost. However, with the content of this PR everything is a lot clear to me. This PR teaches:
|
I'm re-running the jobs to make sure #1706 fixes clippy. |
I think clippy failed because it's working on the head commit, or an old merge. Let's just merge, the type is used, so clippy should pass on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote some of this, and others have checked it, so it's good.
* change `poll_ready()` and `call()` of `ChainVerifier` * add bound, move max_checkpoint_height * add buffers to the checkpoint and block verifiers And rename the chain verifier fields so `block` means `Arc<Block>`, and `block_verifier` means `Buffer<BlockVerifier, ...>`. * Fix the error types * Use `ServiceExt::oneshot` in `ChainVerifier::call` And: * make the code look like the `main` branch as much as possible * document the `poll_ready`/`call` invariant * Use `ServiceExt::oneshot` in `chain::init` Co-authored-by: teor <[email protected]>
Motivation
Work in progress for #1679
Solution
Review
Related Issues
Closes #1679.
Fixes part of #1675.
Follow Up Work
Clippy will fail on this PR until #1706 merges.