mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 02:51:08 +00:00
Do not re-prepare PVFs if not needed (#4211)
Currently, PVFs are re-prepared if any execution environment parameter changes. As we've recently seen on Kusama and Polkadot, that may lead to a severe finality lag because every validator has to re-prepare every PVF. That cannot be avoided altogether; however, we could cease re-preparing PVFs when a change in the execution environment can't lead to a change in the artifact itself. For example, it's clear that changing the execution timeout cannot affect the artifact. In this PR, I'm introducing a separate hash for the subset of execution environment parameters that changes only if a preparation-related parameter changes. It introduces some minor code duplication, but without that, the scope of changes would be much bigger. TODO: - [x] Add a test to ensure the artifact is not re-prepared if non-preparation-related parameter is changed - [x] Add a test to ensure the artifact is re-prepared if a preparation-related parameter is changed - [x] Add comments, warnings, and, possibly, a test to ensure a new parameter ever added to the executor environment parameters will be evaluated by the author of changes with respect to its artifact preparation impact and added to the new hash preimage if needed. Closes #4132
This commit is contained in:
@@ -58,7 +58,7 @@ use crate::{host::PrecheckResultSender, worker_interface::WORKER_DIR_PREFIX};
|
||||
use always_assert::always;
|
||||
use polkadot_node_core_pvf_common::{error::PrepareError, prepare::PrepareStats, pvf::PvfPrepData};
|
||||
use polkadot_parachain_primitives::primitives::ValidationCodeHash;
|
||||
use polkadot_primitives::ExecutorParamsHash;
|
||||
use polkadot_primitives::ExecutorParamsPrepHash;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
@@ -85,22 +85,27 @@ pub fn generate_artifact_path(cache_path: &Path) -> PathBuf {
|
||||
artifact_path
|
||||
}
|
||||
|
||||
/// Identifier of an artifact. Encodes a code hash of the PVF and a hash of executor parameter set.
|
||||
/// Identifier of an artifact. Encodes a code hash of the PVF and a hash of preparation-related
|
||||
/// executor parameter set.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ArtifactId {
|
||||
pub(crate) code_hash: ValidationCodeHash,
|
||||
pub(crate) executor_params_hash: ExecutorParamsHash,
|
||||
pub(crate) executor_params_prep_hash: ExecutorParamsPrepHash,
|
||||
}
|
||||
|
||||
impl ArtifactId {
|
||||
/// Creates a new artifact ID with the given hash.
|
||||
pub fn new(code_hash: ValidationCodeHash, executor_params_hash: ExecutorParamsHash) -> Self {
|
||||
Self { code_hash, executor_params_hash }
|
||||
pub fn new(
|
||||
code_hash: ValidationCodeHash,
|
||||
executor_params_prep_hash: ExecutorParamsPrepHash,
|
||||
) -> Self {
|
||||
Self { code_hash, executor_params_prep_hash }
|
||||
}
|
||||
|
||||
/// Returns an artifact ID that corresponds to the PVF with given executor params.
|
||||
/// Returns an artifact ID that corresponds to the PVF with given preparation-related
|
||||
/// executor parameters.
|
||||
pub fn from_pvf_prep_data(pvf: &PvfPrepData) -> Self {
|
||||
Self::new(pvf.code_hash(), pvf.executor_params().hash())
|
||||
Self::new(pvf.code_hash(), pvf.executor_params().prep_hash())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use polkadot_node_core_pvf::{
|
||||
ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR,
|
||||
};
|
||||
use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult};
|
||||
use polkadot_primitives::{ExecutorParam, ExecutorParams};
|
||||
use polkadot_primitives::{ExecutorParam, ExecutorParams, PvfExecKind, PvfPrepKind};
|
||||
|
||||
use std::{io::Write, time::Duration};
|
||||
use tokio::sync::Mutex;
|
||||
@@ -559,3 +559,73 @@ async fn nonexistent_cache_dir() {
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Checks the the artifact is not re-prepared when the executor environment parameters change
|
||||
// in a way not affecting the preparation
|
||||
#[tokio::test]
|
||||
async fn artifact_does_not_reprepare_on_non_meaningful_exec_parameter_change() {
|
||||
let host = TestHost::new_with_config(|cfg| {
|
||||
cfg.prepare_workers_hard_max_num = 1;
|
||||
})
|
||||
.await;
|
||||
let cache_dir = host.cache_dir.path();
|
||||
|
||||
let set1 = ExecutorParams::default();
|
||||
let set2 =
|
||||
ExecutorParams::from(&[ExecutorParam::PvfExecTimeout(PvfExecKind::Backing, 2500)][..]);
|
||||
|
||||
let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), set1).await.unwrap();
|
||||
|
||||
let md1 = {
|
||||
let mut cache_dir: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect();
|
||||
assert_eq!(cache_dir.len(), 2);
|
||||
let mut artifact_path = cache_dir.pop().unwrap().unwrap();
|
||||
if artifact_path.path().is_dir() {
|
||||
artifact_path = cache_dir.pop().unwrap().unwrap();
|
||||
}
|
||||
std::fs::metadata(artifact_path.path()).unwrap()
|
||||
};
|
||||
|
||||
// FS times are not monotonical so we wait 2 secs here to be sure that the creation time of the
|
||||
// second attifact will be different
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
|
||||
let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), set2).await.unwrap();
|
||||
|
||||
let md2 = {
|
||||
let mut cache_dir: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect();
|
||||
assert_eq!(cache_dir.len(), 2);
|
||||
let mut artifact_path = cache_dir.pop().unwrap().unwrap();
|
||||
if artifact_path.path().is_dir() {
|
||||
artifact_path = cache_dir.pop().unwrap().unwrap();
|
||||
}
|
||||
std::fs::metadata(artifact_path.path()).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(md1.created().unwrap(), md2.created().unwrap());
|
||||
}
|
||||
|
||||
// Checks if the artifact is re-prepared if the re-preparation is needed by the nature of
|
||||
// the execution environment parameters change
|
||||
#[tokio::test]
|
||||
async fn artifact_does_reprepare_on_meaningful_exec_parameter_change() {
|
||||
let host = TestHost::new_with_config(|cfg| {
|
||||
cfg.prepare_workers_hard_max_num = 1;
|
||||
})
|
||||
.await;
|
||||
let cache_dir = host.cache_dir.path();
|
||||
|
||||
let set1 = ExecutorParams::default();
|
||||
let set2 =
|
||||
ExecutorParams::from(&[ExecutorParam::PvfPrepTimeout(PvfPrepKind::Prepare, 60000)][..]);
|
||||
|
||||
let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), set1).await.unwrap();
|
||||
let cache_dir_contents: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect();
|
||||
|
||||
assert_eq!(cache_dir_contents.len(), 2);
|
||||
|
||||
let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), set2).await.unwrap();
|
||||
let cache_dir_contents: Vec<_> = std::fs::read_dir(cache_dir).unwrap().collect();
|
||||
|
||||
assert_eq!(cache_dir_contents.len(), 3); // new artifact has been added
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user