@@ -108,6 +108,7 @@ impl ProcedureMeta {
108108
109109/// Reference counted pointer to [ProcedureMeta].
110110type ProcedureMetaRef = Arc < ProcedureMeta > ;
111+
111112/// Procedure loaded from store.
112113struct LoadedProcedure {
113114 procedure : BoxedProcedure ,
@@ -176,9 +177,20 @@ impl ManagerContext {
176177
177178 /// Load procedure with specific `procedure_id` from cached [ProcedureMessage]s.
178179 fn load_one_procedure ( & self , procedure_id : ProcedureId ) -> Option < LoadedProcedure > {
179- let messages = self . messages . lock ( ) . unwrap ( ) ;
180- let message = messages. get ( & procedure_id) ?;
180+ let message = {
181+ let messages = self . messages . lock ( ) . unwrap ( ) ;
182+ messages. get ( & procedure_id) . cloned ( ) ?
183+ } ;
184+
185+ self . load_one_procedure_from_message ( procedure_id, & message)
186+ }
181187
188+ /// Load procedure from specific [ProcedureMessage].
189+ fn load_one_procedure_from_message (
190+ & self ,
191+ procedure_id : ProcedureId ,
192+ message : & ProcedureMessage ,
193+ ) -> Option < LoadedProcedure > {
182194 let loaders = self . loaders . lock ( ) . unwrap ( ) ;
183195 let loader = loaders. get ( & message. type_name ) . or_else ( || {
184196 logging:: error!(
@@ -344,7 +356,38 @@ impl ProcedureManager for LocalManager {
344356 }
345357
346358 async fn recover ( & self ) -> Result < ( ) > {
347- todo ! ( "Recover procedure and messages" )
359+ logging:: info!( "LocalManager start to recover" ) ;
360+
361+ let procedure_store = ProcedureStore :: new ( self . state_store . clone ( ) ) ;
362+ let messages = procedure_store. load_messages ( ) . await ?;
363+
364+ for ( procedure_id, message) in & messages {
365+ if message. parent_id . is_none ( ) {
366+ // This is the root procedure. We only submit the root procedure as it will
367+ // submit sub-procedures to the manager.
368+ let Some ( loaded_procedure) = self . manager_ctx . load_one_procedure_from_message ( * procedure_id, message) else {
369+ // Try to load other procedures.
370+ continue ;
371+ } ;
372+
373+ logging:: info!(
374+ "Recover root procedure {}-{}, step: {}" ,
375+ loaded_procedure. procedure. type_name( ) ,
376+ procedure_id,
377+ loaded_procedure. step
378+ ) ;
379+
380+ if let Err ( e) = self . submit_root (
381+ * procedure_id,
382+ loaded_procedure. step ,
383+ loaded_procedure. procedure ,
384+ ) {
385+ logging:: error!( e; "Failed to recover procedure {}" , procedure_id) ;
386+ }
387+ }
388+ }
389+
390+ Ok ( ( ) )
348391 }
349392
350393 async fn procedure_state ( & self , procedure_id : ProcedureId ) -> Result < Option < ProcedureState > > {
@@ -380,7 +423,6 @@ mod test_util {
380423
381424#[ cfg( test) ]
382425mod tests {
383- use serde:: { Deserialize , Serialize } ;
384426 use tempdir:: TempDir ;
385427
386428 use super :: * ;
@@ -447,59 +489,109 @@ mod tests {
447489 assert_eq ! ( expect, ctx. procedures_in_tree( & root) ) ;
448490 }
449491
450- #[ test]
451- fn test_register_loader ( ) {
452- let dir = TempDir :: new ( "register" ) . unwrap ( ) ;
453- let config = ManagerConfig {
454- object_store : test_util:: new_object_store ( & dir) ,
455- } ;
456- let manager = LocalManager :: new ( config) ;
492+ #[ derive( Debug ) ]
493+ struct ProcedureToLoad {
494+ content : String ,
495+ }
457496
458- # [ derive ( Debug , Serialize , Deserialize ) ]
459- struct MockData {
460- id : u32 ,
461- content : String ,
497+ # [ async_trait ]
498+ impl Procedure for ProcedureToLoad {
499+ fn type_name ( & self ) -> & str {
500+ "ProcedureToLoad"
462501 }
463502
464- #[ derive( Debug ) ]
465- struct ProcedureToLoad {
466- data : MockData ,
503+ async fn execute ( & mut self , _ctx : & Context ) -> Result < Status > {
504+ Ok ( Status :: Done )
467505 }
468506
469- #[ async_trait]
470- impl Procedure for ProcedureToLoad {
471- fn type_name ( & self ) -> & str {
472- "ProcedureToLoad"
473- }
507+ fn dump ( & self ) -> Result < String > {
508+ Ok ( self . content . clone ( ) )
509+ }
474510
475- async fn execute ( & mut self , _ctx : & Context ) -> Result < Status > {
476- unimplemented ! ( )
477- }
511+ fn lock_key ( & self ) -> Option < LockKey > {
512+ None
513+ }
514+ }
478515
479- fn dump ( & self ) -> Result < String > {
480- Ok ( serde_json:: to_string ( & self . data ) . unwrap ( ) )
516+ impl ProcedureToLoad {
517+ fn new ( content : & str ) -> ProcedureToLoad {
518+ ProcedureToLoad {
519+ content : content. to_string ( ) ,
481520 }
521+ }
482522
483- fn lock_key ( & self ) -> Option < LockKey > {
484- None
485- }
523+ fn loader ( ) -> BoxedProcedureLoader {
524+ let f = |json : & str | {
525+ let procedure = ProcedureToLoad :: new ( json) ;
526+ Ok ( Box :: new ( procedure) as _ )
527+ } ;
528+ Box :: new ( f)
486529 }
530+ }
487531
488- let loader = |json : & str | {
489- let data = serde_json:: from_str ( json) . unwrap ( ) ;
490- let procedure = ProcedureToLoad { data } ;
491- Ok ( Box :: new ( procedure) as _ )
532+ #[ test]
533+ fn test_register_loader ( ) {
534+ let dir = TempDir :: new ( "register" ) . unwrap ( ) ;
535+ let config = ManagerConfig {
536+ object_store : test_util:: new_object_store ( & dir) ,
492537 } ;
538+ let manager = LocalManager :: new ( config) ;
539+
493540 manager
494- . register_loader ( "ProcedureToLoad" , Box :: new ( loader) )
541+ . register_loader ( "ProcedureToLoad" , ProcedureToLoad :: loader ( ) )
495542 . unwrap ( ) ;
496543 // Register duplicate loader.
497544 let err = manager
498- . register_loader ( "ProcedureToLoad" , Box :: new ( loader) )
545+ . register_loader ( "ProcedureToLoad" , ProcedureToLoad :: loader ( ) )
499546 . unwrap_err ( ) ;
500547 assert ! ( matches!( err, Error :: LoaderConflict { .. } ) , "{err}" ) ;
501548 }
502549
550+ #[ tokio:: test]
551+ async fn test_recover ( ) {
552+ let dir = TempDir :: new ( "recover" ) . unwrap ( ) ;
553+ let object_store = test_util:: new_object_store ( & dir) ;
554+ let config = ManagerConfig {
555+ object_store : object_store. clone ( ) ,
556+ } ;
557+ let manager = LocalManager :: new ( config) ;
558+
559+ manager
560+ . register_loader ( "ProcedureToLoad" , ProcedureToLoad :: loader ( ) )
561+ . unwrap ( ) ;
562+
563+ // Prepare data
564+ let procedure_store = ProcedureStore :: from ( object_store. clone ( ) ) ;
565+ let root: BoxedProcedure = Box :: new ( ProcedureToLoad :: new ( "test recover manager" ) ) ;
566+ let root_id = ProcedureId :: random ( ) ;
567+ // Prepare data for the root procedure.
568+ for step in 0 ..3 {
569+ procedure_store
570+ . store_procedure ( root_id, step, & root, None )
571+ . await
572+ . unwrap ( ) ;
573+ }
574+
575+ let child: BoxedProcedure = Box :: new ( ProcedureToLoad :: new ( "a child procedure" ) ) ;
576+ let child_id = ProcedureId :: random ( ) ;
577+ // Prepare data for the child procedure
578+ for step in 0 ..2 {
579+ procedure_store
580+ . store_procedure ( child_id, step, & child, Some ( root_id) )
581+ . await
582+ . unwrap ( ) ;
583+ }
584+
585+ // Recover the manager
586+ manager. recover ( ) . await . unwrap ( ) ;
587+
588+ // The manager should submit the root procedure.
589+ assert ! ( manager. procedure_state( root_id) . await . unwrap( ) . is_some( ) ) ;
590+ // Since the mocked root procedure actually doesn't submit subprocedures, so there is no
591+ // related state.
592+ assert ! ( manager. procedure_state( child_id) . await . unwrap( ) . is_none( ) ) ;
593+ }
594+
503595 #[ tokio:: test]
504596 async fn test_submit_procedure ( ) {
505597 let dir = TempDir :: new ( "submit" ) . unwrap ( ) ;
0 commit comments