Prefer fetching small PoVs from backing group (#7173)

* impl QueryChunkSize

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>

* QueryChunkSize message

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>

* enable fetching from backing group for small pov

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>

* review feedback

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>

* Refactor `bypass_availability_store`

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>

* review feedback

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>

---------

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>
This commit is contained in:
Andrei Sandu
2023-05-05 12:56:54 +03:00
committed by GitHub
parent 4c88b30ee4
commit 2ca3750f0f
6 changed files with 370 additions and 18 deletions
@@ -117,6 +117,44 @@ fn test_harness_chunks_only<T: Future<Output = (VirtualOverseer, RequestResponse
.unwrap();
}
fn test_harness_chunks_if_pov_large<
T: Future<Output = (VirtualOverseer, RequestResponseConfig)>,
>(
test: impl FnOnce(VirtualOverseer, RequestResponseConfig) -> T,
) {
let _ = env_logger::builder()
.is_test(true)
.filter(Some("polkadot_availability_recovery"), log::LevelFilter::Trace)
.try_init();
let pool = sp_core::testing::TaskExecutor::new();
let (context, virtual_overseer) = make_subsystem_context(pool.clone());
let (collation_req_receiver, req_cfg) =
IncomingRequest::get_config_receiver(&ReqProtocolNames::new(&GENESIS_HASH, None));
let subsystem = AvailabilityRecoverySubsystem::with_chunks_if_pov_large(
collation_req_receiver,
Metrics::new_dummy(),
);
let subsystem = subsystem.run(context);
let test_fut = test(virtual_overseer, req_cfg);
futures::pin_mut!(test_fut);
futures::pin_mut!(subsystem);
executor::block_on(future::join(
async move {
let (mut overseer, _req_cfg) = test_fut.await;
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
},
subsystem,
))
.1
.unwrap();
}
const TIMEOUT: Duration = Duration::from_millis(300);
macro_rules! delay {
@@ -249,7 +287,7 @@ impl TestState {
let _ = tx.send(if with_data {
Some(self.available_data.clone())
} else {
println!("SENDING NONE");
gum::debug!("Sending None");
None
});
}
@@ -914,6 +952,169 @@ fn fast_path_backing_group_recovers() {
});
}
#[test]
fn recovers_from_only_chunks_if_pov_large() {
let test_state = TestState::default();
test_harness_chunks_if_pov_large(|mut virtual_overseer, req_cfg| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(ActivatedLeaf {
hash: test_state.current.clone(),
number: 1,
status: LeafStatus::Fresh,
span: Arc::new(jaeger::Span::Disabled),
})),
)
.await;
let (tx, rx) = oneshot::channel();
overseer_send(
&mut virtual_overseer,
AvailabilityRecoveryMessage::RecoverAvailableData(
test_state.candidate.clone(),
test_state.session_index,
Some(GroupIndex(0)),
tx,
),
)
.await;
test_state.test_runtime_api(&mut virtual_overseer).await;
let candidate_hash = test_state.candidate.hash();
assert_matches!(
overseer_recv(&mut virtual_overseer).await,
AllMessages::AvailabilityStore(
AvailabilityStoreMessage::QueryChunkSize(_, tx)
) => {
let _ = tx.send(Some(1000000));
}
);
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
test_state
.test_chunk_requests(
candidate_hash,
&mut virtual_overseer,
test_state.threshold(),
|_| Has::Yes,
)
.await;
// Recovered data should match the original one.
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
let (tx, rx) = oneshot::channel();
// Test another candidate, send no chunks.
let mut new_candidate = dummy_candidate_receipt(dummy_hash());
new_candidate.descriptor.relay_parent = test_state.candidate.descriptor.relay_parent;
overseer_send(
&mut virtual_overseer,
AvailabilityRecoveryMessage::RecoverAvailableData(
new_candidate.clone(),
test_state.session_index,
None,
tx,
),
)
.await;
test_state.test_runtime_api(&mut virtual_overseer).await;
assert_matches!(
overseer_recv(&mut virtual_overseer).await,
AllMessages::AvailabilityStore(
AvailabilityStoreMessage::QueryChunkSize(_, tx)
) => {
let _ = tx.send(Some(1000000));
}
);
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
test_state
.test_chunk_requests(
new_candidate.hash(),
&mut virtual_overseer,
test_state.impossibility_threshold(),
|_| Has::No,
)
.await;
// A request times out with `Unavailable` error.
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
(virtual_overseer, req_cfg)
});
}
#[test]
fn fast_path_backing_group_recovers_if_pov_small() {
let test_state = TestState::default();
test_harness_chunks_if_pov_large(|mut virtual_overseer, req_cfg| async move {
overseer_signal(
&mut virtual_overseer,
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(ActivatedLeaf {
hash: test_state.current.clone(),
number: 1,
status: LeafStatus::Fresh,
span: Arc::new(jaeger::Span::Disabled),
})),
)
.await;
let (tx, rx) = oneshot::channel();
overseer_send(
&mut virtual_overseer,
AvailabilityRecoveryMessage::RecoverAvailableData(
test_state.candidate.clone(),
test_state.session_index,
Some(GroupIndex(0)),
tx,
),
)
.await;
test_state.test_runtime_api(&mut virtual_overseer).await;
let candidate_hash = test_state.candidate.hash();
let who_has = |i| match i {
3 => Has::Yes,
_ => Has::No,
};
assert_matches!(
overseer_recv(&mut virtual_overseer).await,
AllMessages::AvailabilityStore(
AvailabilityStoreMessage::QueryChunkSize(_, tx)
) => {
let _ = tx.send(Some(100));
}
);
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
test_state
.test_full_data_requests(candidate_hash, &mut virtual_overseer, who_has)
.await;
// Recovered data should match the original one.
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
(virtual_overseer, req_cfg)
});
}
#[test]
fn no_answers_in_fast_path_causes_chunk_requests() {
let test_state = TestState::default();