use std::future::{Future, poll_fn};
use std::pin::Pin;
use std::sync::Arc;
use std::task::Poll;
use std::time::Duration;
use crate::combinator::SelectAll;
use crate::cx::{Cx, cap};
use crate::util::{DetHashMap, DetHashSet};
use super::{PlanDag, PlanId, PlanNode, RewriteCertificate, RewritePolicy, RewriteRule};
pub type BoxFut<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
pub type SuccessPred<T> = Arc<dyn Fn(&T) -> bool + Send + Sync>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(usize);
impl NodeId {
#[inline]
#[must_use]
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlanValue<T> {
Scalar(T),
Vector(Vec<T>),
}
impl<T> PlanValue<T> {
pub fn into_scalar(self) -> Result<T, PlanExecError> {
match self {
Self::Scalar(t) => Ok(t),
Self::Vector(_) => Err(PlanExecError::UnexpectedShape),
}
}
pub fn into_vector(self) -> Result<Vec<T>, PlanExecError> {
match self {
Self::Vector(v) => Ok(v),
Self::Scalar(_) => Err(PlanExecError::UnexpectedShape),
}
}
fn into_flat(self) -> Vec<T> {
match self {
Self::Scalar(t) => vec![t],
Self::Vector(v) => v,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlanExecError {
MissingRoot,
MissingChild {
parent: NodeId,
child: NodeId,
},
EmptyChildren {
parent: NodeId,
},
SharedNode {
node: NodeId,
},
InvalidQuorum {
parent: NodeId,
required: usize,
available: usize,
},
Timeout {
node: NodeId,
},
RaceProducedNothing,
FirstOkExhausted {
node: NodeId,
},
QuorumNotMet {
node: NodeId,
achieved: usize,
required: usize,
},
UnexpectedShape,
NotRepresentable {
node: NodeId,
},
}
impl std::fmt::Display for PlanExecError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingRoot => write!(f, "captured plan has no root node"),
Self::MissingChild { parent, child } => write!(
f,
"node {} references missing child {}",
parent.index(),
child.index()
),
Self::EmptyChildren { parent } => {
write!(f, "node {} has no children", parent.index())
}
Self::SharedNode { node } => write!(
f,
"node {} is referenced by more than one parent (plans must be trees)",
node.index()
),
Self::InvalidQuorum {
parent,
required,
available,
} => write!(
f,
"node {} quorum threshold {required} invalid for {available} children",
parent.index()
),
Self::Timeout { node } => write!(f, "node {} timed out", node.index()),
Self::RaceProducedNothing => write!(f, "race produced no result"),
Self::FirstOkExhausted { node } => {
write!(
f,
"node {} first_ok exhausted with no success",
node.index()
)
}
Self::QuorumNotMet {
node,
achieved,
required,
} => write!(
f,
"node {} quorum not met: {achieved}/{required}",
node.index()
),
Self::UnexpectedShape => write!(f, "captured plan produced an unexpected value shape"),
Self::NotRepresentable { node } => write!(
f,
"node {} has no structural PlanDag representation",
node.index()
),
}
}
}
impl std::error::Error for PlanExecError {}
enum CaptureNode<'a, T> {
Leaf {
label: String,
fut: BoxFut<'a, T>,
},
Join(Vec<NodeId>),
Race(Vec<NodeId>),
Timeout {
child: NodeId,
duration: Duration,
},
FirstOk {
children: Vec<NodeId>,
is_success: SuccessPred<T>,
},
Quorum {
children: Vec<NodeId>,
required: usize,
is_success: SuccessPred<T>,
},
}
pub struct PlanCapture<'a, T> {
nodes: Vec<CaptureNode<'a, T>>,
root: Option<NodeId>,
}
impl<T> Default for PlanCapture<'_, T> {
fn default() -> Self {
Self::new()
}
}
pub fn capture<'a, T, F>(build: F) -> Result<ExecPlan<'a, T>, PlanExecError>
where
F: FnOnce(&mut PlanCapture<'a, T>) -> NodeId,
{
let mut builder = PlanCapture::new();
let root = build(&mut builder);
builder.set_root(root);
builder.finish()
}
impl<'a, T> PlanCapture<'a, T> {
#[must_use]
pub fn new() -> Self {
Self {
nodes: Vec::new(),
root: None,
}
}
fn push(&mut self, node: CaptureNode<'a, T>) -> NodeId {
let id = NodeId(self.nodes.len());
self.nodes.push(node);
id
}
pub fn leaf<F>(&mut self, fut: F) -> NodeId
where
F: Future<Output = T> + 'a,
{
let label = format!("leaf{}", self.nodes.len());
self.push(CaptureNode::Leaf {
label,
fut: Box::pin(fut),
})
}
pub fn labeled_leaf<F>(&mut self, label: impl Into<String>, fut: F) -> NodeId
where
F: Future<Output = T> + 'a,
{
self.push(CaptureNode::Leaf {
label: label.into(),
fut: Box::pin(fut),
})
}
pub fn join(&mut self, children: impl IntoIterator<Item = NodeId>) -> NodeId {
self.push(CaptureNode::Join(children.into_iter().collect()))
}
pub fn race(&mut self, children: impl IntoIterator<Item = NodeId>) -> NodeId {
self.push(CaptureNode::Race(children.into_iter().collect()))
}
pub fn timeout(&mut self, child: NodeId, duration: Duration) -> NodeId {
self.push(CaptureNode::Timeout { child, duration })
}
pub fn first_ok<P>(
&mut self,
children: impl IntoIterator<Item = NodeId>,
is_success: P,
) -> NodeId
where
P: Fn(&T) -> bool + Send + Sync + 'static,
{
self.push(CaptureNode::FirstOk {
children: children.into_iter().collect(),
is_success: Arc::new(is_success),
})
}
pub fn quorum<P>(
&mut self,
children: impl IntoIterator<Item = NodeId>,
required: usize,
is_success: P,
) -> NodeId
where
P: Fn(&T) -> bool + Send + Sync + 'static,
{
self.push(CaptureNode::Quorum {
children: children.into_iter().collect(),
required,
is_success: Arc::new(is_success),
})
}
pub fn set_root(&mut self, root: NodeId) {
self.root = Some(root);
}
pub fn finish(self) -> Result<ExecPlan<'a, T>, PlanExecError> {
let Some(root) = self.root else {
return Err(PlanExecError::MissingRoot);
};
let mut ref_count = vec![0usize; self.nodes.len()];
for (idx, node) in self.nodes.iter().enumerate() {
let parent = NodeId(idx);
match node {
CaptureNode::Leaf { .. } => {}
CaptureNode::Join(children)
| CaptureNode::Race(children)
| CaptureNode::FirstOk { children, .. } => {
if children.is_empty() {
return Err(PlanExecError::EmptyChildren { parent });
}
for &c in children {
bump_ref(&mut ref_count, parent, c)?;
}
}
CaptureNode::Quorum {
children, required, ..
} => {
if children.is_empty() {
return Err(PlanExecError::EmptyChildren { parent });
}
if *required == 0 || *required > children.len() {
return Err(PlanExecError::InvalidQuorum {
parent,
required: *required,
available: children.len(),
});
}
for &c in children {
bump_ref(&mut ref_count, parent, c)?;
}
}
CaptureNode::Timeout { child, .. } => {
bump_ref(&mut ref_count, parent, *child)?;
}
}
}
if root.index() >= self.nodes.len() {
return Err(PlanExecError::MissingChild {
parent: root,
child: root,
});
}
Ok(ExecPlan {
nodes: self.nodes,
root,
})
}
}
fn bump_ref(ref_count: &mut [usize], parent: NodeId, child: NodeId) -> Result<(), PlanExecError> {
let slot = ref_count
.get_mut(child.index())
.ok_or(PlanExecError::MissingChild { parent, child })?;
*slot += 1;
if *slot > 1 {
return Err(PlanExecError::SharedNode { node: child });
}
Ok(())
}
pub struct ExecPlan<'a, T> {
nodes: Vec<CaptureNode<'a, T>>,
root: NodeId,
}
enum OwnedNode<'a, T> {
Leaf(BoxFut<'a, T>),
Join(Vec<OwnedNode<'a, T>>),
Race(Vec<OwnedNode<'a, T>>),
Timeout {
child: Box<OwnedNode<'a, T>>,
duration: Duration,
node: NodeId,
},
FirstOk {
children: Vec<OwnedNode<'a, T>>,
is_success: SuccessPred<T>,
node: NodeId,
},
Quorum {
children: Vec<OwnedNode<'a, T>>,
required: usize,
is_success: SuccessPred<T>,
node: NodeId,
},
}
impl<'a, T> ExecPlan<'a, T> {
#[inline]
#[must_use]
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn try_structure(&self) -> Result<PlanDag, PlanExecError> {
let mut dag = PlanDag::new();
let mut mapping = vec![None; self.nodes.len()];
let root = self.structure_from(self.root, &mut dag, &mut mapping)?;
dag.set_root(root);
Ok(dag)
}
fn structure_from(
&self,
id: NodeId,
dag: &mut PlanDag,
mapping: &mut [Option<PlanId>],
) -> Result<PlanId, PlanExecError> {
if let Some(existing) = mapping[id.index()] {
return Ok(existing);
}
let plan_id = match &self.nodes[id.index()] {
CaptureNode::Leaf { label, .. } => dag.leaf(label.clone()),
CaptureNode::Join(children) => {
let mapped = self.structure_children(children, dag, mapping)?;
dag.join(mapped)
}
CaptureNode::Race(children) => {
let mapped = self.structure_children(children, dag, mapping)?;
dag.race(mapped)
}
CaptureNode::Timeout { child, duration } => {
let mapped = self.structure_from(*child, dag, mapping)?;
dag.timeout(mapped, *duration)
}
CaptureNode::FirstOk { .. } | CaptureNode::Quorum { .. } => {
return Err(PlanExecError::NotRepresentable { node: id });
}
};
mapping[id.index()] = Some(plan_id);
Ok(plan_id)
}
fn structure_children(
&self,
children: &[NodeId],
dag: &mut PlanDag,
mapping: &mut [Option<PlanId>],
) -> Result<Vec<PlanId>, PlanExecError> {
children
.iter()
.map(|&c| self.structure_from(c, dag, mapping))
.collect()
}
fn into_owned(mut self) -> OwnedNode<'a, T> {
let mut slots: Vec<Option<CaptureNode<'a, T>>> = self.nodes.drain(..).map(Some).collect();
take_owned(&mut slots, self.root)
}
fn dismantle(
self,
) -> (
PlanDag,
Vec<Option<BoxFut<'a, T>>>,
DetHashMap<PlanId, usize>,
) {
let mut slots: Vec<Option<CaptureNode<'a, T>>> = self.nodes.into_iter().map(Some).collect();
let mut dag = PlanDag::new();
let mut leaf_store: Vec<Option<BoxFut<'a, T>>> = Vec::new();
let mut leaf_pid_to_idx = DetHashMap::default();
let root = dismantle_node(
&mut slots,
self.root,
&mut dag,
&mut leaf_store,
&mut leaf_pid_to_idx,
);
dag.set_root(root);
(dag, leaf_store, leaf_pid_to_idx)
}
#[allow(clippy::future_not_send)]
pub async fn execute<Caps>(self, cx: &Cx<Caps>) -> Result<PlanValue<T>, PlanExecError>
where
Caps: cap::HasTime,
T: 'a,
{
let owned = self.into_owned();
eval(owned, cx).await
}
#[allow(clippy::future_not_send)] pub async fn execute_scalar<Caps>(self, cx: &Cx<Caps>) -> Result<T, PlanExecError>
where
Caps: cap::HasTime,
T: 'a,
{
self.execute(cx).await?.into_scalar()
}
#[allow(clippy::future_not_send)] pub async fn execute_all<Caps>(self, cx: &Cx<Caps>) -> Result<Vec<T>, PlanExecError>
where
Caps: cap::HasTime,
T: 'a,
{
self.execute(cx).await?.into_vector()
}
}
fn take_owned<'a, T>(slots: &mut [Option<CaptureNode<'a, T>>], id: NodeId) -> OwnedNode<'a, T> {
let node = slots[id.index()]
.take()
.expect("validated tree: each node is taken exactly once");
match node {
CaptureNode::Leaf { fut, .. } => OwnedNode::Leaf(fut),
CaptureNode::Join(children) => {
OwnedNode::Join(children.into_iter().map(|c| take_owned(slots, c)).collect())
}
CaptureNode::Race(children) => {
OwnedNode::Race(children.into_iter().map(|c| take_owned(slots, c)).collect())
}
CaptureNode::Timeout { child, duration } => OwnedNode::Timeout {
child: Box::new(take_owned(slots, child)),
duration,
node: id,
},
CaptureNode::FirstOk {
children,
is_success,
} => OwnedNode::FirstOk {
children: children.into_iter().map(|c| take_owned(slots, c)).collect(),
is_success,
node: id,
},
CaptureNode::Quorum {
children,
required,
is_success,
} => OwnedNode::Quorum {
children: children.into_iter().map(|c| take_owned(slots, c)).collect(),
required,
is_success,
node: id,
},
}
}
fn dismantle_node<'a, T>(
slots: &mut [Option<CaptureNode<'a, T>>],
id: NodeId,
dag: &mut PlanDag,
leaf_store: &mut Vec<Option<BoxFut<'a, T>>>,
leaf_pid_to_idx: &mut DetHashMap<PlanId, usize>,
) -> PlanId {
let node = slots[id.index()]
.take()
.expect("validated representable tree: each node is taken exactly once");
match node {
CaptureNode::Leaf { label, fut } => {
let pid = dag.leaf(label);
let idx = leaf_store.len();
leaf_store.push(Some(fut));
leaf_pid_to_idx.insert(pid, idx);
pid
}
CaptureNode::Join(children) => {
let mut kids = Vec::with_capacity(children.len());
for c in children {
kids.push(dismantle_node(slots, c, dag, leaf_store, leaf_pid_to_idx));
}
dag.join(kids)
}
CaptureNode::Race(children) => {
let mut kids = Vec::with_capacity(children.len());
for c in children {
kids.push(dismantle_node(slots, c, dag, leaf_store, leaf_pid_to_idx));
}
dag.race(kids)
}
CaptureNode::Timeout { child, duration } => {
let c = dismantle_node(slots, child, dag, leaf_store, leaf_pid_to_idx);
dag.timeout(c, duration)
}
CaptureNode::FirstOk { .. } | CaptureNode::Quorum { .. } => {
unreachable!("dismantle runs only after try_structure confirms representability")
}
}
}
type EvalFut<'b, T> = Pin<Box<dyn Future<Output = Result<PlanValue<T>, PlanExecError>> + 'b>>;
fn eval<'a, 'b, T, Caps>(node: OwnedNode<'a, T>, cx: &'b Cx<Caps>) -> EvalFut<'b, T>
where
'a: 'b,
T: 'a,
Caps: cap::HasTime,
{
Box::pin(async move {
match node {
OwnedNode::Leaf(fut) => Ok(PlanValue::Scalar(fut.await)),
OwnedNode::Join(children) => {
let futs: Vec<EvalFut<'b, T>> = children.into_iter().map(|c| eval(c, cx)).collect();
let results = drive_all(futs).await;
let mut flat = Vec::new();
for r in results {
flat.extend(r?.into_flat());
}
Ok(PlanValue::Vector(flat))
}
OwnedNode::Race(children) => {
let futs: Vec<EvalFut<'b, T>> = children.into_iter().map(|c| eval(c, cx)).collect();
match SelectAll::new(futs).await {
Ok((inner, _idx)) => inner,
Err(_) => Err(PlanExecError::RaceProducedNothing),
}
}
OwnedNode::Timeout {
child,
duration,
node,
} => {
let child_fut = eval(*child, cx);
let now = cx.now();
match crate::time::timeout(now, duration, child_fut).await {
Ok(inner) => inner,
Err(_elapsed) => Err(PlanExecError::Timeout { node }),
}
}
OwnedNode::FirstOk {
children,
is_success,
node,
} => {
let futs: Vec<EvalFut<'b, T>> = children.into_iter().map(|c| eval(c, cx)).collect();
let results = drive_all(futs).await;
for r in results {
let value = r?;
let scalar = value.into_scalar()?;
if is_success(&scalar) {
return Ok(PlanValue::Scalar(scalar));
}
}
Err(PlanExecError::FirstOkExhausted { node })
}
OwnedNode::Quorum {
children,
required,
is_success,
node,
} => {
let futs: Vec<EvalFut<'b, T>> = children.into_iter().map(|c| eval(c, cx)).collect();
let results = drive_all(futs).await;
let mut wins = Vec::new();
for r in results {
let scalar = r?.into_scalar()?;
if is_success(&scalar) {
wins.push(scalar);
}
}
if wins.len() >= required {
Ok(PlanValue::Vector(wins))
} else {
Err(PlanExecError::QuorumNotMet {
node,
achieved: wins.len(),
required,
})
}
}
}
})
}
#[allow(clippy::future_not_send)] async fn drive_all<'f, R>(mut futs: Vec<Pin<Box<dyn Future<Output = R> + 'f>>>) -> Vec<R> {
let mut outs: Vec<Option<R>> = (0..futs.len()).map(|_| None).collect();
poll_fn(|task_cx| {
let mut pending = false;
for (i, fut) in futs.iter_mut().enumerate() {
if outs[i].is_none() {
match fut.as_mut().poll(task_cx) {
Poll::Ready(v) => outs[i] = Some(v),
Poll::Pending => pending = true,
}
}
}
if pending {
Poll::Pending
} else {
Poll::Ready(())
}
})
.await;
outs.into_iter()
.map(|o| o.expect("drive_all resolved only after every branch was Ready"))
.collect()
}
const REWRITE_RULE_MENU: [RewriteRule; 6] = [
RewriteRule::JoinAssoc,
RewriteRule::RaceAssoc,
RewriteRule::JoinCommute,
RewriteRule::RaceCommute,
RewriteRule::TimeoutMin,
RewriteRule::DedupRaceJoin,
];
#[derive(Debug)]
pub struct OptimizedExecution<T> {
pub value: PlanValue<T>,
pub certificate: Option<RewriteCertificate>,
pub rewritten: bool,
pub fired_rules: Vec<RewriteRule>,
pub fallback_reason: Option<String>,
}
#[allow(clippy::future_not_send)] pub async fn capture_optimized<'a, T, Caps, F>(
cx: &Cx<Caps>,
build: F,
) -> Result<OptimizedExecution<T>, PlanExecError>
where
F: FnOnce(&mut PlanCapture<'a, T>) -> NodeId,
Caps: cap::HasTime,
T: 'a,
{
capture_optimized_with_policy(cx, build, RewritePolicy::conservative()).await
}
#[allow(clippy::future_not_send)] pub async fn capture_optimized_with_policy<'a, T, Caps, F>(
cx: &Cx<Caps>,
build: F,
policy: RewritePolicy,
) -> Result<OptimizedExecution<T>, PlanExecError>
where
F: FnOnce(&mut PlanCapture<'a, T>) -> NodeId,
Caps: cap::HasTime,
T: 'a,
{
let policy_label = if policy == RewritePolicy::conservative() {
"conservative"
} else if policy == RewritePolicy::assume_all() {
"aggressive"
} else {
"requested-policy"
};
let plan = capture(build)?;
match plan.try_structure() {
Ok(_) => {}
Err(PlanExecError::NotRepresentable { .. }) => {
let value = plan.execute(cx).await?;
return Ok(OptimizedExecution {
value,
certificate: None,
rewritten: false,
fired_rules: Vec::new(),
fallback_reason: Some(
"plan contains first_ok/quorum; no structural rewrite IR".to_string(),
),
});
}
Err(e) => return Err(e),
}
let (original_dag, mut leaf_store, leaf_pid_to_idx) = plan.dismantle();
let mut rewritten_dag = original_dag.clone();
let (report, certificate) = rewritten_dag.apply_rewrites_certified(policy, &REWRITE_RULE_MENU);
let fired_rules: Vec<RewriteRule> = report.steps().iter().map(|step| step.rule).collect();
let (exec_dag, rewritten, fallback_reason) = if report.is_empty() {
(
&original_dag,
false,
Some(format!("no {policy_label} rewrite applied")),
)
} else if rewritten_dag.validate().is_err() {
(
&original_dag,
false,
Some("rewritten plan failed structural validation; ran original".to_string()),
)
} else if leaves_conserved(&rewritten_dag, &leaf_pid_to_idx) {
(&rewritten_dag, true, None)
} else {
(
&original_dag,
false,
Some(
"rewrite not leaf-conserving (a one-shot leaf would be duplicated or dropped); ran original"
.to_string(),
),
)
};
let root = exec_dag.root().ok_or(PlanExecError::MissingRoot)?;
let owned = owned_from_dag(exec_dag, root, &mut leaf_store, &leaf_pid_to_idx)?;
let value = eval(owned, cx).await?;
Ok(OptimizedExecution {
value,
certificate: Some(certificate),
rewritten,
fired_rules,
fallback_reason,
})
}
fn leaves_conserved(dag: &PlanDag, leaf_pid_to_idx: &DetHashMap<PlanId, usize>) -> bool {
let Some(root) = dag.root() else {
return false;
};
let mut seen = DetHashSet::default();
let mut leaf_indices = Vec::new();
if !collect_tree_leaves(dag, root, &mut seen, &mut leaf_indices, leaf_pid_to_idx) {
return false;
}
if leaf_indices.len() != leaf_pid_to_idx.len() {
return false;
}
leaf_indices.sort_unstable();
leaf_indices.dedup();
leaf_indices.len() == leaf_pid_to_idx.len()
}
fn collect_tree_leaves(
dag: &PlanDag,
id: PlanId,
seen: &mut DetHashSet<PlanId>,
leaf_indices: &mut Vec<usize>,
leaf_pid_to_idx: &DetHashMap<PlanId, usize>,
) -> bool {
if !seen.insert(id) {
return false; }
match dag.node(id) {
None => false,
Some(PlanNode::Leaf { .. }) => match leaf_pid_to_idx.get(&id) {
Some(idx) => {
leaf_indices.push(*idx);
true
}
None => false,
},
Some(PlanNode::Join { children } | PlanNode::Race { children }) => children
.iter()
.all(|&c| collect_tree_leaves(dag, c, seen, leaf_indices, leaf_pid_to_idx)),
Some(PlanNode::Timeout { child, .. }) => {
collect_tree_leaves(dag, *child, seen, leaf_indices, leaf_pid_to_idx)
}
}
}
fn owned_from_dag<'a, T>(
dag: &PlanDag,
id: PlanId,
leaf_store: &mut [Option<BoxFut<'a, T>>],
leaf_pid_to_idx: &DetHashMap<PlanId, usize>,
) -> Result<OwnedNode<'a, T>, PlanExecError> {
let node = NodeId(id.index());
match dag.node(id).ok_or(PlanExecError::MissingChild {
parent: node,
child: node,
})? {
PlanNode::Leaf { .. } => {
let idx = *leaf_pid_to_idx
.get(&id)
.ok_or(PlanExecError::MissingChild {
parent: node,
child: node,
})?;
let fut = leaf_store[idx]
.take()
.ok_or(PlanExecError::SharedNode { node })?;
Ok(OwnedNode::Leaf(fut))
}
PlanNode::Join { children } => {
let mut kids = Vec::with_capacity(children.len());
for &c in children {
kids.push(owned_from_dag(dag, c, leaf_store, leaf_pid_to_idx)?);
}
Ok(OwnedNode::Join(kids))
}
PlanNode::Race { children } => {
let mut kids = Vec::with_capacity(children.len());
for &c in children {
kids.push(owned_from_dag(dag, c, leaf_store, leaf_pid_to_idx)?);
}
Ok(OwnedNode::Race(kids))
}
PlanNode::Timeout { child, duration } => Ok(OwnedNode::Timeout {
child: Box::new(owned_from_dag(dag, *child, leaf_store, leaf_pid_to_idx)?),
duration: *duration,
node,
}),
}
}