Add Error message when sync is still in progress.#9475
Conversation
|
It looks like @seunlanlege hasn't signed our Contributor License Agreement, yet.
You can read and sign our full Contributor License Agreement at the following URL: https://cla.parity.io Once you've signed, please reply to this thread with Many thanks, Parity Technologies CLA Bot |
|
[clabot:check] |
|
It looks like @seunlanlege signed our Contributor License Agreement. 👍 Many thanks, Parity Technologies CLA Bot |
| if let Ok(SyncStatus::Info(_)) = self.syncing() { | ||
| // alas, we're still syncing! | ||
| warn!("Attempted to fetch transaction via hash while sync is in progress"); | ||
| return Box::new(future::err(errors::ancient_block_missing())) |
There was a problem hiding this comment.
Isn't it possible to be in a syncing state even while we're quite close to the head of the chain? As a user I think I'd be surprised to see an error about "ancient" blocks if I were essentially synched. Maybe the error message should be something more generic like Block is unavailable?
| if res.is_none() { | ||
| if let Ok(SyncStatus::Info(_)) = self.syncing() { | ||
| // alas, we're still syncing! | ||
| warn!("Call made to transaction_by_block_hash_and_index while sync is in progress"); |
There was a problem hiding this comment.
Not sure that using the full method name here is a good idea. How about "RPC call is unavailable while synching".
| if let Ok(SyncStatus::Info(_)) = self.syncing() { | ||
| // alas, we're still syncing! | ||
| warn!("Attempted to fetch transaction via hash while sync is in progress"); | ||
| return Box::new(future::err(errors::ancient_block_missing())) |
There was a problem hiding this comment.
Same comments from above apply here.
Maybe there's a way to add these warnings to all RPC calls higher up in the call chain so we don't have to pollute each method with log/warn calls?
andresilva
left a comment
There was a problem hiding this comment.
I don't think this is correct and I don't think it's easy to implement this properly. The main issue is that if I give you an hash how do you distinguish from you not having the data (because you're still syncing) or the data not existing in the first place? Also the current checks for self.syncing() only check for normal sync as far as I remember, and even though I'm syncing I may already have the data available, e.g. I'm synced to block #2000 (but still syncing up to say #4000) and you ask for data from block #1000.
|
(reposting from private chat) I guess for some RPCs we could implement a best-effort approach. For example to fetch transaction by hash:
Can we use this strategy for all RPC methods? Do we want to implement something like this? |
tomusdrw
left a comment
There was a problem hiding this comment.
As in #9188 (comment) it's not really possible to implement this with significant changes to snapshot format. What's more I don't really think it's a good idea to store the tx hashes in snapshots anyway (perhaps we could store at most a bloom filter).
That said, I like @andresilva's suggestion to return an error instead of None in case we know we have not fetched ancient blocks yet (note it's very different from syncing, so self.syncing() is not the right method to use here).
If Andre's suggestion gets implemented we should also consider disabling that behaviour in case someone is consciously running with --no-ancient-blocks.
|
#7411 might be a bit more straightforward - we have the block number there so we can just return an error in case |
tomusdrw
left a comment
There was a problem hiding this comment.
Still incorrect, but better. We're almost there.
| RpcResult<Option<T>> { | ||
| move |res| { | ||
| if res.is_none() { | ||
| if let Ok(SyncStatus::Info(_)) = sync_status { |
There was a problem hiding this comment.
The syncing state doesn't really matter, unless there is a variant that tells you that: "The chain is incomplete - We are still syncing/importing ancient blocks" (Which is completely different than regular sync, some more details about warp sync: https://wiki.parity.io/Warp-Sync.html)
| if let BlockNumber::Num(number) = num { | ||
| match self.client.chain_info().best_block_number { | ||
| BlockNumber::Num(best_block_number) => { | ||
| if num < best_block_number { |
There was a problem hiding this comment.
This is quite wrong, actually these are the only queries that make sense at all, since:
- In database we store blocks from
[0...best_block_number] - Querying anything from that range should succeed
- Querying anything greater than
best_block_numberwill always fail
With ancient blocks the case is that the database is not complete, so we have a situation where:
- We have
[0..best_ancient_block] ... [first_block .. best_block] - The latter range has been restored together with the snapshot
- The former range has been downloaded during ancient block import
- Any query that tries to get
num < best_block_numberand fails (i.e. returnsNone) most likely falls into(best_ancient_block .. first_block)range, so we should detect that and return the error only in such case.
| fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>> { | ||
| Box::new(future::done(self.rich_block(num.into(), include_txs))) | ||
| let result = self.check_block_existence(num) | ||
| .and_then(|_| self.rich_block(num.into(), include_txs)); |
There was a problem hiding this comment.
To make it correct you should:
- First query the block
- If it's
Noneandnum < best_block_numberreturn ancient block unavailable error.
|
Despite trying hard, I still can't manage to know all issues by number :) Having a human understandable description and title for a PR is therefore requested! |
andresilva
left a comment
There was a problem hiding this comment.
Overall LGTM. Just minor comments.
| return Err(Error { | ||
| code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), | ||
| // this error message feels pretty straightforward to me. ¯\_(ツ)_/¯ | ||
| message: "We cannot tell for sure if the thing you requested is not available or we just don't have it".into(), |
There was a problem hiding this comment.
Probably better to be explicit about it? "Ancient block sync is still in progress."
| let BlockChainInfo { ancient_block_hash, first_block_hash, .. } = client.chain_info(); | ||
| // block information was requested, but unfortunately we couldn't find it and there | ||
| // are gaps in the database ethcore/src/blockchain/blockchain.rs:202 | ||
| if ancient_block_hash.is_some() && first_block_hash.is_some() { |
There was a problem hiding this comment.
I think first_block_hash will always be some.
| pub fn unavailable_block() -> Error { | ||
| Error { | ||
| code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), | ||
| message: "Block is unavailable".into(), |
There was a problem hiding this comment.
Probably better to have a more explicit message about ancient block sync?
|
Please rebase/merge master to fix conflicts. |
d8c0cd2 to
904daaf
Compare
tomusdrw
left a comment
There was a problem hiding this comment.
lgtm, minor grumble regarding error message - we can distinguish between:
- Knowing for sure that the blocks is not there because of ancient sync
- Assuming that it might be not found, because we don't have all the blocks yet.
Also I believe if we merge that we should also have an option to disable this behaviour since it's a breaking change (instead of returning Ok(None) we return an Error)
| { | ||
| move |response| { | ||
| if response.is_none() { | ||
| let BlockChainInfo { ancient_block_hash, first_block_hash, .. } = client.chain_info(); |
There was a problem hiding this comment.
first_block_hash is unused
| // block information was requested, but unfortunately we couldn't find it and there | ||
| // are gaps in the database ethcore/src/blockchain/blockchain.rs:202 | ||
| if ancient_block_hash.is_some() { | ||
| return Err(unavailable_block()) |
There was a problem hiding this comment.
This is a bit different than check_block_number_existence. Here we are unsure if the block is not valid or was not found, in the former we actually have certainty that the block is missing because we are still syncing ancient blocks.
TBH I think we should express that in the error message as well.
sorpaas
left a comment
There was a problem hiding this comment.
Current changes LGTM. A few grumbles. And I think there're also other APIs in eth_ namespace that can apply the same strategy:
block_transaction_count_by_hashblock_transaction_count_by_numberblock_uncles_count_by_hashblock_uncles_count_by_numberuncle_by_block_hash_and_indexuncle_by_block_number_and_index
| keep_alive: Option<bool>, | ||
| experimental_rpcs: Option<bool>, | ||
| poll_lifetime: Option<u32>, | ||
| jsonrpc_allow_missing_blocks: Option<bool>, |
There was a problem hiding this comment.
nit: maybe name this just allow_missing_blocks? We need the jsonrpc prefix for args but they're already under the category for configs.
| deps.miner.clone(), | ||
| $nonces, | ||
| deps.gas_price_percentile, | ||
| ); |
There was a problem hiding this comment.
nit: indentation is wrong.
| } | ||
| } | ||
| } | ||
| }}; |
There was a problem hiding this comment.
nit: this indentation looks weird
|
Also there's a test failure: |
block_transaction_count_by_hash
block_transaction_count_by_number
block_uncles_count_by_hash
block_uncles_count_by_number
uncle_by_block_hash_and_index
uncle_by_block_number_and_index
fix PR grumbles
revert config name Co-Authored-By: seunlanlege <seunlanlege@gmail.com>
revert cli arg Co-Authored-By: seunlanlege <seunlanlege@gmail.com>
revert config name Co-Authored-By: seunlanlege <seunlanlege@gmail.com>
| let best_block = self.client.chain_info().best_block_number; | ||
| if let Some(receipt) = self.miner.pending_receipt(best_block, &hash) { | ||
| return Box::new(future::ok(Some(receipt.into()))); | ||
| match (self.miner.pending_receipt(best_block, &hash), self.options.allow_pending_receipt_query) { |
There was a problem hiding this comment.
This will unnecessarily call self.miner.pending_receipt even when self.options.allow_pending_receipt_query is false. I would suggest to change it back to the old two if form.
| } | ||
|
|
||
| fn transaction_receipt(&self, hash: RpcH256) -> BoxFuture<Option<Receipt>> { | ||
| let best_block = self.client.chain_info().best_block_number; |
There was a problem hiding this comment.
This is only used inside if self.options.allow_pending_receipt_query {, so maybe it's better to move it back.
* closes #9188 * check_for_unavailable_block in eth_getTransactionByHash eth_getTransactionByBlockHashAndIndex eth_getTransactionByBlockNumberAndIndex eth_getTransactionReceipt * check for unavailable block in eth_getBlockByNumber * corrected checks for unavailable_block * check for block gaps in db * corrected error messages * corrected error information * added allow-empty-block-result cli flag * address grumbles * --jsonrpc-allow-missing-blocks * fix tests * added checks to block_transaction_count_by_hash block_transaction_count_by_number block_uncles_count_by_hash block_uncles_count_by_number uncle_by_block_hash_and_index uncle_by_block_number_and_index fix PR grumbles * Update parity/cli/mod.rs revert config name Co-Authored-By: seunlanlege <seunlanlege@gmail.com> * Update parity/cli/mod.rs revert cli arg Co-Authored-By: seunlanlege <seunlanlege@gmail.com> * Apply suggestions from code review revert config name Co-Authored-By: seunlanlege <seunlanlege@gmail.com> * fix PR grumbles * fix more PR grumbles
fix #9188