Skip to content

Rust orchestrator - #433

Merged
johnchildren merged 25 commits into
mainfrom
rust-orchestrator
Apr 24, 2026
Merged

Rust orchestrator#433
johnchildren merged 25 commits into
mainfrom
rust-orchestrator

Conversation

@johnchildren

Copy link
Copy Markdown
Collaborator

No description provided.

Comment thread tierkreis/src/executor/interface.rs Outdated
}

#[derive(Clone, Debug, PartialEq)]
pub enum Status {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Probably overkill, but what about other states, e.g. created?
Is there a way of indicating the number of retries here / somewhere else

}

#[derive(Debug, PartialEq)]
pub struct WorkerSpec {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

might be useful to know if a worker is internal (aka from python with @worker.task) or external (a binary we don't control).
Also have a mention about the expected behavior of workers: binary worker_cal_args etc.

@johnchildren
johnchildren marked this pull request as ready for review April 20, 2026 16:03
Comment thread tierkreis/src/asset_storage/interface.rs Outdated
/// The [`AssetStorage`] defines the minimum methods required for Assets to be stored.
///
/// The interface is essentially a key-value store, keyed by [`AssetKey`]s.
pub trait AssetStorage: Send + Sync {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

something like invalidate might be useful in the future

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Makes sense, I guess we may also want to be able to do something like specify retention periods or similar?

Comment thread tierkreis/src/asset_storage/inmemory.rs Outdated
Comment thread tierkreis/src/event.rs
Comment thread tierkreis/src/server.rs
/// The names and locations of the inputs to the task.
pub inputs: HashMap<String, AssetSpec>,
/// The names of the outputs of the task.
// TODO: We can find this out from the workers

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

but might be the case that we don't need all of the outputs the worker declares

///
/// If not specified the [Executor] will save the outputs to a default
/// [`AssetStorage`][crate::asset_storage::AssetStorage] instead.
pub output_storage_name: Option<String>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

implies that all task outputs will be written to the same storage.
Thinking of the ntchem example file outputs could be different from the return value of the binary

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's something we could do later, I just did the easiest thing

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It also doesn't need to be optional - we do always know what the output storage name should be at this point

Comment on lines +44 to +47
pub struct WorkerSpec {
/// The name of the Worker.
pub worker_name: String,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I assume this is tbd.? you mention meta data further down but I guess it's supposed to be more than the name? At least in the current design, could point to the api/give an appreviated version of it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Indeed, I would like to flesh this out with information about which tasks are available and what their signatures are.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It kind of links in with trying to improve the IDL as well


/// [`InMemoryEnvironmentSpec`] determines the default execution environment of
/// [`InMemoryExecutor`] or what is requested as part of a [`TaskPlan`].
// TODO: It's perhaps a bit unclear what this means for in-memory execution?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I understand. I guess this would be something like available modules?
But if a worker is installed this would mean everything should be acaialble, else how would it run?
And I assume we don't target stuff like numa information

Comment thread tierkreis/src/executor/inmemory.rs Outdated
}

/// The [Executor] defines the minimum methods required for Task execution.
pub trait Executor: Send + Sync {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we will need something like register_worker

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Could be useful, but it might depend a bit about how long we expect Executor objects to live, something to discuss in person tomorrow perhaps?

Comment thread tierkreis/src/executor/subprocess.rs
Comment thread tierkreis/src/executor/subprocess.rs Outdated
Comment thread tierkreis/src/executor/subprocess.rs Outdated
Comment on lines +230 to +231
// This guarantees the tasks will complete in order as far as the runtime
// is concerned.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

is this a necessary requrirement? I thought only the dispatch order matters (i.e. all inputs must be ready)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It is not a requirement and I'm also pretty inconsistent about it, I guess ideally we might want to pass the task dependencies into the executor as well but I didn't think enough about the correct way to do this.

Comment thread tierkreis/src/executor/subprocess.rs Outdated
pub struct SubprocessExecutor {
event_sender: mpsc::Sender<Event>,
event_receiver: Mutex<Option<mpsc::Receiver<Event>>>,
cancel_senders: Mutex<HashMap<u32, oneshot::Sender<()>>>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

whats the reason this is oneshot and not mpsc?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

There's a slight inconsistency in the way I implemented the subprocess and the inmemory executors that I can probably fix where the subprocess executor futures are just spawned into the background and they handle their own cancellation, whereas the inmemory one has a central cancellation channel. As such cancellation is performed with a oneshot channel for the subprocess one as only one message needs to be sent.

Comment on lines +62 to +71
let asset_storage_registry_lock = asset_storage_registry
.read()
.map_err(|err| miette!("Failed to lock AssetStorageRegistry for reading: {err}"))?;
if !asset_storage_registry_lock.contains_key(default_storage_name) {
return Err(miette!("default_storage_name not in registry"));
}

if !executor_registry.contains_key(default_executor_name) {
return Err(miette!("default_executor_name not in registry"));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

depending how often we need this we could change this to a function on the registry?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, right now it just a type alias, but we could wrap it up into a struct if there's a small number of methods that we use like this.

let default_executor_name = &self.default_executor_name;
let executor = self
.executor_registry
.get(&self.default_executor_name)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

do I understand correctly that we always choose the same executor?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Currently yes, I would like to be able to set the executor on the node but I am not sure what the best way to do this is. As in, should be metadata or similar?

}
Node::Output {} => {
// Notify that the outputs are ready
let mut event_sender = self.event_sender.clone();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

who is consuming this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

All of the events end up in the stream provided by listen. Ideally I want to then use this stream to build up the execution state somewhere which then drives the next round of execution.

.into_diagnostic()?;
}
Node::Eval {} => {
// spawn a sub-orchestrator?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think this one possible way but then we need to deal with nested orchestrators

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I've been thinking about this today and I think we want to avoided nested orchestrators but it needs some thinking about how we handle locations.

Comment thread tierkreis/src/orchestrator.rs Outdated
Comment thread tierkreis/src/orchestrator.rs
fn load(&self, key: &AssetKey) -> miette::Result<Vec<u8>> {
let location = self.location(key);
let file = File::open(self.location(key))
let mut file = File::open(self.location(key))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why is this mut now?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Because the read trait needs it to change where it is looking at in the file, I think before we were passing ownership to serde_json which was doing it's own mutation

@johnchildren
johnchildren merged commit 70369e8 into main Apr 24, 2026
13 checks passed
@johnchildren
johnchildren deleted the rust-orchestrator branch April 24, 2026 14:36
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.

2 participants