@@ -203,7 +203,8 @@ struct InspectorSummaryJson {
203203 rpcs : Vec < String > ,
204204 queue_size : u32 ,
205205 is_database_enabled : bool ,
206- is_workflow_enabled : bool ,
206+ #[ serde( rename = "isWorkflowEnabled" ) ]
207+ workflow_supported : bool ,
207208 workflow_history : Option < JsonValue > ,
208209}
209210
@@ -857,12 +858,12 @@ impl RegistryDispatcher {
857858 ( http:: Method :: GET , "/inspector/workflow-history" ) => self
858859 . inspector_workflow_history ( instance)
859860 . await
860- . and_then ( |( is_workflow_enabled , history) | {
861+ . and_then ( |( workflow_supported , history) | {
861862 json_http_response (
862863 StatusCode :: OK ,
863864 & json ! ( {
864865 "history" : history,
865- "isWorkflowEnabled" : is_workflow_enabled ,
866+ "isWorkflowEnabled" : workflow_supported ,
866867 } ) ,
867868 )
868869 } ) ,
@@ -872,14 +873,14 @@ impl RegistryDispatcher {
872873 Err ( response) => return Ok ( Some ( response) ) ,
873874 } ;
874875 self
875- . inspector_replay_workflow ( instance, body. entry_id )
876+ . inspector_workflow_replay ( instance, body. entry_id )
876877 . await
877- . and_then ( |( is_workflow_enabled , history) | {
878+ . and_then ( |( workflow_supported , history) | {
878879 json_http_response (
879880 StatusCode :: OK ,
880881 & json ! ( {
881882 "history" : history,
882- "isWorkflowEnabled" : is_workflow_enabled ,
883+ "isWorkflowEnabled" : workflow_supported ,
883884 } ) ,
884885 )
885886 } )
@@ -1008,7 +1009,7 @@ impl RegistryDispatcher {
10081009 . inspect_messages ( )
10091010 . await
10101011 . context ( "list queue messages for inspector summary" ) ?;
1011- let ( is_workflow_enabled , workflow_history) = self
1012+ let ( workflow_supported , workflow_history) = self
10121013 . inspector_workflow_history ( instance)
10131014 . await
10141015 . context ( "load inspector workflow summary" ) ?;
@@ -1019,7 +1020,7 @@ impl RegistryDispatcher {
10191020 rpcs : inspector_rpcs ( instance) ,
10201021 queue_size : queue_messages. len ( ) . try_into ( ) . unwrap_or ( u32:: MAX ) ,
10211022 is_database_enabled : instance. ctx . sql ( ) . runtime_config ( ) . is_ok ( ) ,
1022- is_workflow_enabled ,
1023+ workflow_supported ,
10231024 workflow_history,
10241025 } )
10251026 }
@@ -1031,27 +1032,27 @@ impl RegistryDispatcher {
10311032 self
10321033 . inspector_workflow_history_bytes ( instance)
10331034 . await
1034- . map ( |( is_workflow_enabled , history) | {
1035+ . map ( |( workflow_supported , history) | {
10351036 (
1036- is_workflow_enabled ,
1037+ workflow_supported ,
10371038 history
10381039 . map ( |payload| decode_cbor_json_or_null ( & payload) )
10391040 . filter ( |value| !value. is_null ( ) ) ,
10401041 )
10411042 } )
10421043 }
10431044
1044- async fn inspector_replay_workflow (
1045+ async fn inspector_workflow_replay (
10451046 & self ,
10461047 instance : & ActorTaskHandle ,
10471048 entry_id : Option < String > ,
10481049 ) -> Result < ( bool , Option < JsonValue > ) > {
10491050 self
1050- . inspector_replay_workflow_bytes ( instance, entry_id)
1051+ . inspector_workflow_replay_bytes ( instance, entry_id)
10511052 . await
1052- . map ( |( is_workflow_enabled , history) | {
1053+ . map ( |( workflow_supported , history) | {
10531054 (
1054- is_workflow_enabled ,
1055+ workflow_supported ,
10551056 history
10561057 . map ( |payload| decode_cbor_json_or_null ( & payload) )
10571058 . filter ( |value| !value. is_null ( ) ) ,
@@ -1063,35 +1064,38 @@ impl RegistryDispatcher {
10631064 & self ,
10641065 instance : & ActorTaskHandle ,
10651066 ) -> Result < ( bool , Option < Vec < u8 > > ) > {
1066- let history = instance
1067+ let result = instance
10671068 . ctx
10681069 . internal_keep_awake ( dispatch_workflow_history_through_task (
10691070 & instance. dispatch ,
10701071 instance. factory . config ( ) . dispatch_command_inbox_capacity ,
10711072 ) )
10721073 . await
1073- . context ( "load inspector workflow history" ) ? ;
1074+ . context ( "load inspector workflow history" ) ;
10741075
1075- Ok ( ( true , history ) )
1076+ workflow_dispatch_result ( result )
10761077 }
10771078
1078- async fn inspector_replay_workflow_bytes (
1079+ async fn inspector_workflow_replay_bytes (
10791080 & self ,
10801081 instance : & ActorTaskHandle ,
10811082 entry_id : Option < String > ,
10821083 ) -> Result < ( bool , Option < Vec < u8 > > ) > {
1083- let history = instance
1084+ let result = instance
10841085 . ctx
1085- . internal_keep_awake ( dispatch_workflow_replay_through_task (
1086+ . internal_keep_awake ( dispatch_workflow_replay_request_through_task (
10861087 & instance. dispatch ,
10871088 instance. factory . config ( ) . dispatch_command_inbox_capacity ,
10881089 entry_id,
10891090 ) )
10901091 . await
1091- . context ( "replay inspector workflow history" ) ?;
1092- instance. inspector . record_workflow_history_updated ( ) ;
1092+ . context ( "replay inspector workflow history" ) ;
1093+ let ( workflow_supported, history) = workflow_dispatch_result ( result) ?;
1094+ if workflow_supported {
1095+ instance. inspector . record_workflow_history_updated ( ) ;
1096+ }
10931097
1094- Ok ( ( true , history) )
1098+ Ok ( ( workflow_supported , history) )
10951099 }
10961100
10971101 async fn inspector_database_schema ( & self , ctx : & ActorContext ) -> Result < JsonValue > {
@@ -1950,25 +1954,25 @@ impl RegistryDispatcher {
19501954 ) ) )
19511955 }
19521956 inspector_protocol:: ClientMessage :: WorkflowHistoryRequest ( request) => {
1953- let ( is_workflow_enabled , history) =
1957+ let ( workflow_supported , history) =
19541958 self . inspector_workflow_history_bytes ( instance) . await ?;
19551959 Ok ( Some ( InspectorServerMessage :: WorkflowHistoryResponse (
19561960 inspector_protocol:: WorkflowHistoryResponse {
19571961 rid : request. id ,
19581962 history,
1959- is_workflow_enabled ,
1963+ workflow_supported ,
19601964 } ,
19611965 ) ) )
19621966 }
19631967 inspector_protocol:: ClientMessage :: WorkflowReplayRequest ( request) => {
1964- let ( is_workflow_enabled , history) = self
1965- . inspector_replay_workflow_bytes ( instance, request. entry_id )
1968+ let ( workflow_supported , history) = self
1969+ . inspector_workflow_replay_bytes ( instance, request. entry_id )
19661970 . await ?;
19671971 Ok ( Some ( InspectorServerMessage :: WorkflowReplayResponse (
19681972 inspector_protocol:: WorkflowReplayResponse {
19691973 rid : request. id ,
19701974 history,
1971- is_workflow_enabled ,
1975+ workflow_supported ,
19721976 } ,
19731977 ) ) )
19741978 }
@@ -2004,7 +2008,7 @@ impl RegistryDispatcher {
20042008 & self ,
20052009 instance : & ActorTaskHandle ,
20062010 ) -> Result < InspectorServerMessage > {
2007- let ( is_workflow_enabled , workflow_history) =
2011+ let ( workflow_supported , workflow_history) =
20082012 self . inspector_workflow_history_bytes ( instance) . await ?;
20092013 let queue_size = self . inspector_current_queue_size ( instance) . await ?;
20102014 Ok ( InspectorServerMessage :: Init (
@@ -2016,7 +2020,7 @@ impl RegistryDispatcher {
20162020 is_database_enabled : instance. ctx . sql ( ) . runtime_config ( ) . is_ok ( ) ,
20172021 queue_size,
20182022 workflow_history,
2019- is_workflow_enabled ,
2023+ workflow_supported ,
20202024 } ,
20212025 ) )
20222026 }
@@ -2845,7 +2849,7 @@ async fn dispatch_workflow_history_through_task(
28452849 . context ( "actor task stopped before workflow history dispatch reply was sent" ) ?
28462850}
28472851
2848- async fn dispatch_workflow_replay_through_task (
2852+ async fn dispatch_workflow_replay_request_through_task (
28492853 dispatch : & mpsc:: Sender < DispatchCommand > ,
28502854 capacity : usize ,
28512855 entry_id : Option < String > ,
@@ -2868,6 +2872,21 @@ async fn dispatch_workflow_replay_through_task(
28682872 . context ( "actor task stopped before workflow replay dispatch reply was sent" ) ?
28692873}
28702874
2875+ fn workflow_dispatch_result (
2876+ result : Result < Option < Vec < u8 > > > ,
2877+ ) -> Result < ( bool , Option < Vec < u8 > > ) > {
2878+ match result {
2879+ Ok ( history) => Ok ( ( true , history) ) ,
2880+ Err ( error) if is_dropped_reply_error ( & error) => Ok ( ( false , None ) ) ,
2881+ Err ( error) => Err ( error) ,
2882+ }
2883+ }
2884+
2885+ fn is_dropped_reply_error ( error : & anyhow:: Error ) -> bool {
2886+ let error = RivetError :: extract ( error) ;
2887+ error. group ( ) == "actor" && error. code ( ) == "dropped_reply"
2888+ }
2889+
28712890async fn dispatch_subscribe_request (
28722891 ctx : & ActorContext ,
28732892 conn : ConnHandle ,
@@ -2893,6 +2912,42 @@ fn inspector_anyhow_response(error: anyhow::Error) -> HttpResponse {
28932912 inspector_error_response ( status, error. group ( ) , error. code ( ) , error. message ( ) )
28942913}
28952914
2915+ #[ cfg( test) ]
2916+ mod tests {
2917+ use super :: workflow_dispatch_result;
2918+ use crate :: error:: ActorLifecycle as ActorLifecycleError ;
2919+
2920+ #[ test]
2921+ fn workflow_dispatch_result_marks_handled_workflow_as_enabled ( ) {
2922+ assert_eq ! (
2923+ workflow_dispatch_result( Ok ( Some ( vec![ 1 , 2 , 3 ] ) ) ) . expect( "workflow dispatch should succeed" ) ,
2924+ ( true , Some ( vec![ 1 , 2 , 3 ] ) ) ,
2925+ ) ;
2926+ assert_eq ! (
2927+ workflow_dispatch_result( Ok ( None ) ) . expect( "workflow dispatch should succeed" ) ,
2928+ ( true , None ) ,
2929+ ) ;
2930+ }
2931+
2932+ #[ test]
2933+ fn workflow_dispatch_result_treats_dropped_reply_as_disabled ( ) {
2934+ assert_eq ! (
2935+ workflow_dispatch_result( Err ( ActorLifecycleError :: DroppedReply . build( ) ) )
2936+ . expect( "dropped reply should map to workflow disabled" ) ,
2937+ ( false , None ) ,
2938+ ) ;
2939+ }
2940+
2941+ #[ test]
2942+ fn workflow_dispatch_result_preserves_non_dropped_reply_errors ( ) {
2943+ let error = workflow_dispatch_result ( Err ( ActorLifecycleError :: Destroying . build ( ) ) )
2944+ . expect_err ( "non-dropped reply errors should be preserved" ) ;
2945+ let error = rivet_error:: RivetError :: extract ( & error) ;
2946+ assert_eq ! ( error. group( ) , "actor" ) ;
2947+ assert_eq ! ( error. code( ) , "destroying" ) ;
2948+ }
2949+ }
2950+
28962951fn inspector_error_response (
28972952 status : StatusCode ,
28982953 group : & str ,
0 commit comments