@@ -117,6 +117,35 @@ impl Actor for IndexingPipeline {
117117 self . handle ( Supervise , ctx) . await ?;
118118 Ok ( ( ) )
119119 }
120+
121+ async fn finalize (
122+ & mut self ,
123+ _exit_status : & ActorExitStatus ,
124+ _ctx : & ActorContext < Self > ,
125+ ) -> anyhow:: Result < ( ) > {
126+ // We update the observation to ensure our last "black box" observation
127+ // is up to date.
128+ if let Some ( handles) = & self . handles {
129+ let ( doc_processor_counters, indexer_counters, uploader_counters, publisher_counters) = join ! (
130+ handles. doc_processor. observe( ) ,
131+ handles. indexer. observe( ) ,
132+ handles. uploader. observe( ) ,
133+ handles. publisher. observe( ) ,
134+ ) ;
135+ self . statistics = self
136+ . previous_generations_statistics
137+ . clone ( )
138+ . add_actor_counters (
139+ & * doc_processor_counters,
140+ & * indexer_counters,
141+ & * uploader_counters,
142+ & * publisher_counters,
143+ )
144+ . set_generation ( self . statistics . generation )
145+ . set_num_spawn_attempts ( self . statistics . num_spawn_attempts ) ;
146+ }
147+ Ok ( ( ) )
148+ }
120149}
121150
122151impl IndexingPipeline {
@@ -475,13 +504,15 @@ mod tests {
475504 use std:: path:: PathBuf ;
476505 use std:: sync:: Arc ;
477506
478- use quickwit_actors:: Universe ;
479- use quickwit_config:: { IndexingSettings , SourceParams } ;
507+ use quickwit_actors:: { Command , Universe } ;
508+ use quickwit_config:: { IndexingSettings , SourceParams , VoidSourceParams } ;
480509 use quickwit_doc_mapper:: default_doc_mapper_for_test;
481510 use quickwit_metastore:: { IndexMetadata , MetastoreError , MockMetastore } ;
482511 use quickwit_storage:: RamStorage ;
483512
484513 use super :: { IndexingPipeline , * } ;
514+ use crate :: actors:: merge_pipeline:: { MergePipeline , MergePipelineParams } ;
515+ use crate :: merge_policy:: default_merge_policy;
485516 use crate :: models:: IndexingDirectory ;
486517
487518 #[ test]
@@ -674,4 +705,82 @@ mod tests {
674705 assert_eq ! ( pipeline_statistics. num_published_splits, 1 ) ;
675706 Ok ( ( ) )
676707 }
708+
709+ #[ tokio:: test]
710+ async fn test_merge_pipeline_does_not_stop_on_indexing_pipeline_failure ( ) -> anyhow:: Result < ( ) >
711+ {
712+ let mut metastore = MockMetastore :: default ( ) ;
713+ metastore
714+ . expect_index_metadata ( )
715+ . withf ( |index_id| index_id == "test-index" )
716+ . returning ( |_| {
717+ Ok ( IndexMetadata :: for_test (
718+ "test-index" ,
719+ "ram:///indexes/test-index" ,
720+ ) )
721+ } ) ;
722+ metastore
723+ . expect_list_splits ( )
724+ . returning ( |_, _, _, _| Ok ( Vec :: new ( ) ) ) ;
725+ let universe = Universe :: new ( ) ;
726+ let node_id = "test-node" ;
727+ let metastore = Arc :: new ( metastore) ;
728+ let doc_mapper = Arc :: new ( default_doc_mapper_for_test ( ) ) ;
729+ let pipeline_id = IndexingPipelineId {
730+ index_id : "test-index" . to_string ( ) ,
731+ source_id : "test-source" . to_string ( ) ,
732+ node_id : node_id. to_string ( ) ,
733+ pipeline_ord : 0 ,
734+ } ;
735+ let source_config = SourceConfig {
736+ source_id : "test-source" . to_string ( ) ,
737+ num_pipelines : 1 ,
738+ enabled : true ,
739+ source_params : SourceParams :: Void ( VoidSourceParams ) ,
740+ } ;
741+ let storage = Arc :: new ( RamStorage :: default ( ) ) ;
742+ let split_store = IndexingSplitStore :: create_without_local_store ( storage. clone ( ) ) ;
743+ let merge_pipeline_params = MergePipelineParams {
744+ pipeline_id : pipeline_id. clone ( ) ,
745+ doc_mapper : doc_mapper. clone ( ) ,
746+ indexing_directory : IndexingDirectory :: for_test ( ) . await ,
747+ metastore : metastore. clone ( ) ,
748+ split_store : split_store. clone ( ) ,
749+ merge_policy : default_merge_policy ( ) ,
750+ max_concurrent_split_uploads : 2 ,
751+ merge_max_io_num_bytes_per_sec : None ,
752+ } ;
753+ let merge_pipeline = MergePipeline :: new ( merge_pipeline_params) ;
754+ let merge_planner_mailbox = merge_pipeline. merge_planner_mailbox ( ) . clone ( ) ;
755+ let ( _merge_pipeline_mailbox, merge_pipeline_handler) =
756+ universe. spawn_builder ( ) . spawn ( merge_pipeline) ;
757+ let indexing_pipeline_params = IndexingPipelineParams {
758+ pipeline_id,
759+ doc_mapper,
760+ source_config,
761+ indexing_directory : IndexingDirectory :: for_test ( ) . await ,
762+ indexing_settings : IndexingSettings :: for_test ( ) ,
763+ metastore : metastore. clone ( ) ,
764+ queues_dir_path : PathBuf :: from ( "./queues" ) ,
765+ storage,
766+ split_store,
767+ max_concurrent_split_uploads_index : 4 ,
768+ max_concurrent_split_uploads_merge : 5 ,
769+ merge_planner_mailbox : merge_planner_mailbox. clone ( ) ,
770+ } ;
771+ let indexing_pipeline = IndexingPipeline :: new ( indexing_pipeline_params) ;
772+ let ( _indexing_pipeline_mailbox, indexing_pipeline_handler) =
773+ universe. spawn_builder ( ) . spawn ( indexing_pipeline) ;
774+ assert_eq ! ( indexing_pipeline_handler. observe( ) . await . generation, 1 ) ;
775+ // Let's shutdown the indexer, this will trigger the the indexing pipeline failure and the
776+ // restart.
777+ let indexer = universe. get :: < Indexer > ( ) . into_iter ( ) . next ( ) . unwrap ( ) ;
778+ indexer. send_message ( Command :: Quit ) . await . unwrap ( ) ;
779+ tokio:: time:: sleep ( Duration :: from_secs ( 2 ) ) . await ;
780+ // Check indexing pipeline has restarted.
781+ assert_eq ! ( indexing_pipeline_handler. observe( ) . await . generation, 2 ) ;
782+ // Check that the merge pipeline is still up.
783+ assert_eq ! ( merge_pipeline_handler. health( ) , Health :: Healthy ) ;
784+ Ok ( ( ) )
785+ }
677786}
0 commit comments