@@ -2,8 +2,9 @@ use crate::{
22 core:: orchestrator:: agent:: { EventPayload , start_service} ,
33 uniffi:: {
44 error:: { OrcaError , Result , selector} ,
5- model:: PodJob ,
5+ model:: { PodJob , PodResult } ,
66 orchestrator:: { Orchestrator , Status , docker:: LocalDockerOrchestrator } ,
7+ store:: { Store as _, filestore:: LocalFileStore } ,
78 } ,
89} ;
910use derive_more:: Display ;
@@ -17,6 +18,19 @@ use tokio::task::JoinSet;
1718use uniffi;
1819use zenoh;
1920
21+ /// A response, similar to Rust's `Result` but casting error to `String`.
22+ ///
23+ /// This is a workaround due to `UniFFI` limitations when trying to send a collection of `Result`
24+ /// over the CFFI boundary e.g. concurrent calls where it is necessary to know the status of each
25+ /// request to determine what to retry.
26+ #[ derive( uniffi:: Enum ) ]
27+ pub enum Response {
28+ /// Success
29+ Ok ,
30+ /// Error cast to `String`
31+ Err ( String ) ,
32+ }
33+
2034/// Client to connect to an execution agent within a coordinated fleet. Connection optimized/rerouted by Zenoh.
2135#[ expect(
2236 clippy:: field_scoped_visibility_modifiers,
@@ -58,14 +72,14 @@ impl AgentClient {
5872 }
5973 /// Submit many pod jobs to be processed in parallel.
6074 /// Return order will match inputs, casting outputs to `String` (since `uniffi` doesn't support sending unwrapped `Result`s).
61- pub async fn submit_pod_jobs ( & self , pod_jobs : Vec < Arc < PodJob > > ) -> Vec < String > {
75+ pub async fn submit_pod_jobs ( & self , pod_jobs : Vec < Arc < PodJob > > ) -> Vec < Response > {
6276 join_all ( pod_jobs. iter ( ) . map ( |pod_job| async {
6377 match self
6478 . publish ( & format ! ( "request/pod_job/{}" , pod_job. hash) , pod_job)
6579 . await
6680 {
67- Ok ( ( ) ) => "ok" . into ( ) ,
68- Err ( error) => error. to_string ( ) ,
81+ Ok ( ( ) ) => Response :: Ok ,
82+ Err ( error) => Response :: Err ( error. to_string ( ) ) ,
6983 }
7084 } ) )
7185 . await
@@ -126,13 +140,18 @@ impl Agent {
126140 /// # Errors
127141 ///
128142 /// Will stop and return an error if encounters an error while processing any pod job request.
129- pub async fn start ( & self , namespace_lookup : & HashMap < String , PathBuf > ) -> Result < ( ) > {
143+ #[ expect( clippy:: excessive_nesting, reason = "Nesting manageable." ) ]
144+ pub async fn start (
145+ & self ,
146+ namespace_lookup : & HashMap < String , PathBuf > ,
147+ available_store : Option < Arc < LocalFileStore > > ,
148+ ) -> Result < ( ) > {
130149 let mut services = JoinSet :: new ( ) ;
131150 services. spawn ( start_service (
132151 Arc :: new ( self . clone ( ) ) ,
133152 "request/pod_job/**" . to_owned ( ) ,
134153 namespace_lookup. clone ( ) ,
135- |input : & PodJob | EventPayload :: Request ( input . clone ( ) ) ,
154+ |pod_job : & PodJob | EventPayload :: Request ( pod_job . clone ( ) ) ,
136155 async |agent, inner_namespace_lookup, _, pod_job| {
137156 let pod_run = agent
138157 . orchestrator
@@ -152,6 +171,33 @@ impl Agent {
152171 client. publish ( response_topic, & pod_result) . await
153172 } ,
154173 ) ) ;
174+ if let Some ( store) = available_store {
175+ services. spawn ( start_service (
176+ Arc :: new ( self . clone ( ) ) ,
177+ "success/pod_job/**" . to_owned ( ) ,
178+ namespace_lookup. clone ( ) ,
179+ |pod_result : & PodResult | EventPayload :: Success ( pod_result. clone ( ) ) ,
180+ {
181+ let inner_store = Arc :: clone ( & store) ;
182+ async move |_, _, _, pod_result| {
183+ inner_store. save_pod_result ( & pod_result) ?;
184+ Ok ( ( ) )
185+ }
186+ } ,
187+ async |_, ( ) | Ok ( ( ) ) ,
188+ ) ) ;
189+ services. spawn ( start_service (
190+ Arc :: new ( self . clone ( ) ) ,
191+ "failure/pod_job/**" . to_owned ( ) ,
192+ namespace_lookup. clone ( ) ,
193+ |pod_result : & PodResult | EventPayload :: Failure ( pod_result. clone ( ) ) ,
194+ async move |_, _, _, pod_result| {
195+ store. save_pod_result ( & pod_result) ?;
196+ Ok ( ( ) )
197+ } ,
198+ async |_, ( ) | Ok ( ( ) ) ,
199+ ) ) ;
200+ }
155201 services
156202 . join_next ( )
157203 . await
0 commit comments