-
Notifications
You must be signed in to change notification settings - Fork 790
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
Client: Warning on not possible further block execution #1065
Conversation
Codecov Report
Flags with carried forward coverage won't be shown. Click here to find out more. |
return await this.initAndLock<number>( | ||
async (): Promise<number> => { | ||
const headHash = this._heads[name] || this._genesis | ||
let lastBlock: Block | undefined |
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've also renamed some variable names for better clarity, not sure why this is show with such a large gap here, might be a bit confusing, but code flow remains the same here.
|
||
if (!block) { | ||
break | ||
} |
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 could be removed since _getBlock()
is not allowing for an undefined
or null
return but is just throwing a NotFoundError
when no block is found (which is caught later on).
Apart from this code flow is the same here as well (just switched the if
clause order in the catch
block for better readability, that's all).
@@ -5,13 +5,13 @@ import VM from './index' | |||
/** | |||
* @ignore | |||
*/ | |||
export default async function runBlockchain(this: VM, blockchain?: Blockchain, maxBlocks?: number) { |
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 would argue that return value can be changed this way here (remaining non-breaking), since no one will have done something with the void
return value before and just used await vm.runBlockchain()
. So the updated return value is more of an optional extra thing now which now can be used if makes sense.
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.
great, sounds good, could the same reasoning be used for the return type of iterator
? I think I would find void | number
confusing.
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.
Hmm, not for the interface I guess, since this would mean that if someone implements that interface it would break their implementation. 😕 I've added this (remove void
return type part) to #1024 though, and the iterator
implementation itself has no void return type.
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.
When thinking about it I'm afraid that it's rather the other way around and we need to also return Promise<void | number>
in the VM for the non-breaking release. Otherwise if someone keeps their own Blockchain
implementation with the void
return and uses this for the VM.runBlockchain()
method, this will break on TypeScript
since the method is expecting a chained return value of number
for the output.
…re capable iterator result evaluation, expand interface (backwards-compatible)
8b0693b
to
e88af94
Compare
…nBlockchain(), client logger warning on no further block execution
52c8071
to
617d62f
Compare
… with Blockchain implemenations with an iterator void return value
617d62f
to
f2bbb08
Compare
It would be nice to figure out how the DB got corrupted. It could possibly be that we do not explicitly flush the blockchain DB once we kill the client, so part of it stays in memory? We'd have to explicitly flush the DB once we get a kill signal. |
if (error.type !== 'NotFoundError') { | ||
throw error | ||
const headBlockNumber = await this.dbManager.hashToNumber(headHash) | ||
const nextBlockNumber = headBlockNumber.addn(1) |
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.
const nextBlockNumber = headBlockNumber.addn(1) | |
const nextBlockNumber = headBlockNumber.iaddn(1) |
...not sure this change would be meaningful but iaddn
is used in the loop below to increment nextBlockNumber
[EDIT - this difference pre-dates the PR fwiw)
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.
LGTM!!
Doesn't this bug also indicate we have a problem in our block fetcher? If the 'bad block' gets deleted from the chain, then we should re-request the block from our peers (by block number, not by block hash). So, if a block gets deleted, send a request to our peers to retrieve the deleted block number(s). |
What will happen if we are at the tip of the chain? Won't we then get this warning, which should not get logged, as there simply is no valid block available at this point in time? |
Need to merge for some subsequent work, will answer the remaining questions. |
@jochem-brouwer I think "tip of the chain" behavior generally needs some dedicated work on its own - once we reach a tip 😋 - would very much expect this not to work on first run. |
My own client (blockchain) DB is currently in a state where block execution is not further done due to a somewhat inconsistent db state. In the client this gets noticeable by the execution messages not further showing up, there are only the block import logs displayed.
I could identify the root cause of the no-execution behavior: in the
Blockchain
iterator function there is no block found in the DB following the current iterator head. Therefore no blocks are executed. I have no idea though how and when the blockchain DB got into this state. I do know though that I've been there (in this state) a couple of times before with block execution stopping.This PR is approaching this on a low level by not changing any logic or behavior but as a first step providing feedback when block execution is not further processing. For this it:
Blockchain.iterator()
interface to allow for anumber
value to be returned (so now allowingPromise<void | number>
, keepingvoid
is for backwards compatibility) and changing our own interface implementation in the blockchain class to return the number of blocks which have been actually iterated. This will likely generally be useful for a number of use cases.VM.runBlockchain()
method now similarly also returns the number of iterated blocksOutput looks like this:
This should allow to better tracing/debugging this in the future and there is now a clear consumer feedback if this behavior occurs.
We might want to decide if we want to further act upon this case, a possibility would be to set the latest blockchain head to the VM iterator head, so that sync will restart from there. I hesitated to directly integrate since it is not clear what corrupted the DB in the first place. We can decide this independently from this PR though.