Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

[WIP] Pvf host prechecking support #4101

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions node/core/pvf/src/artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ impl Artifacts {
}

/// Returns the state of the given artifact by its ID.
pub fn artifact_state(&self, artifact_id: &ArtifactId) -> Option<&ArtifactState> {
self.artifacts.get(artifact_id)
}

/// Returns mutable state of the given artifact by its ID.
pub fn artifact_state_mut(&mut self, artifact_id: &ArtifactId) -> Option<&mut ArtifactState> {
self.artifacts.get_mut(artifact_id)
}
Expand Down
15 changes: 15 additions & 0 deletions node/core/pvf/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.

use parity_scale_codec::{Decode, Encode};

/// An error reported during the prechecking routine.
#[derive(Debug, Clone, Encode, Decode)]
pub enum PrecheckError {
/// Failed to precheck the PVF due to the time limit.
TimedOut,
/// Failed to precheck the PVF due to the memory limit.
MemoryLimitReached,
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we using this anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, I don't know yet how to catch this kind of error, even though there's a memory limit in executor configuration, I don't see any public interface that tells you preparation exceeded it

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, I think that's irrelevant.

The memory limit in the executor is only for the execution. There is no option to limit the memory amount consumed by the compilation. That's actually one of the reasons we do it in a separate process in the first place.

/// Compilation error occurred.
CompileError(String),
/// Couldn't serve the request due to some kind of internal error.
Internal(String),
}

/// A error raised during validation of the candidate.
#[derive(Debug, Clone)]
pub enum ValidationError {
Expand Down
47 changes: 2 additions & 45 deletions node/core/pvf/src/execute/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
artifacts::{ArtifactId, ArtifactPathId},
host::ResultSender,
metrics::Metrics,
worker_common::{IdleWorker, WorkerHandle},
worker_common::{IdleWorker, Worker, WorkerData, WorkerHandle, Workers},
InvalidCandidate, ValidationError, LOG_TARGET,
};
use async_std::path::PathBuf;
Expand All @@ -32,9 +32,7 @@ use futures::{
Future, FutureExt,
};
use slotmap::HopSlotMap;
use std::{collections::VecDeque, fmt, time::Duration};

slotmap::new_key_type! { struct Worker; }
use std::{collections::VecDeque, time::Duration};

#[derive(Debug)]
pub enum ToQueue {
Expand All @@ -53,47 +51,6 @@ struct ExecuteJob {
result_tx: ResultSender,
}

struct WorkerData {
idle: Option<IdleWorker>,
handle: WorkerHandle,
}

impl fmt::Debug for WorkerData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WorkerData(pid={})", self.handle.id())
}
}

struct Workers {
/// The registry of running workers.
running: HopSlotMap<Worker, WorkerData>,

/// The number of spawning but not yet spawned workers.
spawn_inflight: usize,

/// The maximum number of workers queue can have at once.
capacity: usize,
}

impl Workers {
fn can_afford_one_more(&self) -> bool {
self.spawn_inflight + self.running.len() < self.capacity
}

fn find_available(&self) -> Option<Worker> {
self.running
.iter()
.find_map(|d| if d.1.idle.is_some() { Some(d.0) } else { None })
}

/// Find the associated data by the worker token and extract it's [`IdleWorker`] token.
///
/// Returns `None` if either worker is not recognized or idle token is absent.
fn claim_idle(&mut self, worker: Worker) -> Option<IdleWorker> {
self.running.get_mut(worker)?.idle.take()
}
}

enum QueueEvent {
Spawn(IdleWorker, WorkerHandle),
StartWork(Worker, Outcome, ArtifactId, ResultSender),
Expand Down
12 changes: 10 additions & 2 deletions node/core/pvf/src/executor_intf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! Interface to the Substrate Executor

use sc_executor_common::{
error::WasmError,
runtime_blob::RuntimeBlob,
wasm_runtime::{InvokeMethod, WasmModule as _},
};
Expand Down Expand Up @@ -73,7 +74,7 @@ const CONFIG: Config = Config {
};

/// Runs the prevalidation on the given code. Returns a [`RuntimeBlob`] if it succeeds.
pub fn prevalidate(code: &[u8]) -> Result<RuntimeBlob, sc_executor_common::error::WasmError> {
pub fn prevalidate(code: &[u8]) -> Result<RuntimeBlob, WasmError> {
let blob = RuntimeBlob::new(code)?;
// It's assumed this function will take care of any prevalidation logic
// that needs to be done.
Expand All @@ -84,10 +85,17 @@ pub fn prevalidate(code: &[u8]) -> Result<RuntimeBlob, sc_executor_common::error

/// Runs preparation on the given runtime blob. If successful, it returns a serialized compiled
/// artifact which can then be used to pass into [`execute`].
pub fn prepare(blob: RuntimeBlob) -> Result<Vec<u8>, sc_executor_common::error::WasmError> {
pub fn prepare(blob: RuntimeBlob) -> Result<Vec<u8>, WasmError> {
sc_executor_wasmtime::prepare_runtime_artifact(blob, &CONFIG.semantics)
}

/// Runs [`prepare`] routine on a single thread.
pub fn prepare_single_threaded(blob: RuntimeBlob) -> Result<Vec<u8>, WasmError> {
let mut semantics = CONFIG.semantics;
semantics.parallel_compilation = false;
sc_executor_wasmtime::prepare_runtime_artifact(blob, &semantics)
}

/// Executes the given PVF in the form of a compiled artifact and returns the result of execution
/// upon success.
///
Expand Down
Loading