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

Fix candidate tracking on async backing #675

Merged
merged 21 commits into from
Apr 19, 2024

Conversation

AndreiEres
Copy link
Collaborator

@AndreiEres AndreiEres commented Mar 5, 2024

Fixes #673

Major changes

  • Instead of a single current candidate, we track [included, backed] candidates for each assigned core.
  • We set the included status based on chain events, which we store in the collector.
  • Dead code removed

@AndreiEres AndreiEres requested a review from sandreim March 6, 2024 13:54
@AndreiEres AndreiEres force-pushed the AndreiEres/async-backing-candidates branch from a67d1e1 to 8a2e5bb Compare March 6, 2024 13:58
@@ -40,7 +40,7 @@ pub struct SubxtTracker {
/// A new session index.
new_session: Option<u32>,
/// Information about current parachain block we track.
current_candidate: ParachainBlockInfo,
candidates: Vec<ParachainBlockInfo>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a vec of these, which wraps a vec of states. There should be one state per parachain that is tracked.

From parachain consensus perspective we now allow a new candidate to be backed at an occupied core (while the previous candidate is pending avilability).

I'd expect to see this reflected here somehow.

parachain-tracer/src/parachain_block_info.rs Show resolved Hide resolved
@@ -38,58 +34,55 @@ pub struct ParachainBlockInfo {
pub assigned_core: Option<u32>,
/// Core occupation status.
pub core_occupied: bool,
/// The current state.
state: Vec<ParachainBlockState>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why more objects of state instead of one ?

@AndreiEres AndreiEres changed the title Fix candidate tracking on async backing [WIP] Fix candidate tracking on async backing Mar 7, 2024
@AndreiEres AndreiEres changed the title [WIP] Fix candidate tracking on async backing Fix candidate tracking on async backing Mar 11, 2024
if candidate.is_idle() {
progress.events.push(ParachainConsensusEvent::SkippedSlot);
stats.on_skipped_slot(progress);
metrics.on_skipped_slot(progress);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

insert continue . we don't need to check next conditions.

progress.events.push(ParachainConsensusEvent::Backed(candidate_hash));
stats.on_backed();
metrics.on_backed(self.para_id);
if candidate.is_backed() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seem to be so many ifs here, a match would be better if we had an enum for the candidate state.

fn is_just_backed(&self) -> bool {
self.last_backed_at_block_number.is_some() &&
self.last_backed_at_block_number == self.current_relay_block.map(|v| v.num)
}

fn is_slow_availability(&self) -> bool {
self.current_candidate.core_occupied &&
fn is_slow_availability(&self, core: u32) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a good time to make sure we have tests covering all of these functions and ensure they work as expected. The condition below seems brittle.

let max_bits = self.validators_indices(block_hash, storage).await?.len() as u32;
let candidate = self.current_candidate_mut(core).expect("Checked above");

candidate.current_availability_bits = extract_availability_bits_count(&bitfields, core);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the candidate was backed at current block, the bits should not be set at all. I'd assume we want to do this only if !is_just_backed, below.

}
}
if last_included.is_some() {
self.relay_forks.last_mut().expect("must have relay fork").included_candidate = last_included;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this expect panic ?

Comment on lines 252 to 259
if let Some(candidate_hash) = candidate.candidate_hash {
if let Some(stored_candidate) = storage.candidate(candidate_hash).await {
if stored_candidate.is_included() {
candidate.set_included();
last_included = candidate.candidate_hash;
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if let Some(candidate_hash) = candidate.candidate_hash {
if let Some(stored_candidate) = storage.candidate(candidate_hash).await {
if stored_candidate.is_included() {
candidate.set_included();
last_included = candidate.candidate_hash;
}
}
}
let Some(candidate_hash) = candidate.candidate_hash else {
continue
}
let Some(stored_candidate) = storage.candidate(candidate_hash).await else {
continue
}
if stored_candidate.is_included() {
candidate.set_included();
last_included = candidate.candidate_hash;
}

.map(|v| ParachainBlockInfo::candidate_hash(&v));
let mut used_cores = vec![];
for candidate_hash in candidate_hashes {
if let Some(core) = self.candidate_core(candidate_hash, storage).await {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use let Some(..) to avoid nesting.

metrics.on_skipped_slot(progress);
},
ParachainBlockState::Backed =>
if let Some(candidate_hash) = candidate.candidate_hash {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove the option ? Doesn't seem to make sense to have the state backed but no hash.

Copy link
Collaborator Author

@AndreiEres AndreiEres Apr 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, removing ParachainBlockState::Idle.

metrics.on_backed(self.para_id);
},
ParachainBlockState::PendingAvailability => {
if self.is_slow_availability(candidate.assigned_core.expect("must be assigned")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would expect panic ? I'd avoid doing this since it reduces the uptime of the monitor. Let's log errors instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should never panic, because a backed candidate info should include assigned core.

@AndreiEres
Copy link
Collaborator Author

Updated, removing Idle state from candidate's state. Used None and Some(block_info). So we removed unnecessary .expect()s.

@AndreiEres AndreiEres merged commit 5f05614 into master Apr 19, 2024
6 checks passed
@AndreiEres AndreiEres deleted the AndreiEres/async-backing-candidates branch April 19, 2024 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Async backing enabled chains break introspector_pc_backed_count metric
2 participants