1use async_trait::async_trait;
2use concepts::ExecutionFailureKind;
3use concepts::ExecutionId;
4use concepts::ExecutionMetadata;
5use concepts::FunctionMetadata;
6use concepts::TrapKind;
7use concepts::storage::DbErrorWrite;
8use concepts::storage::HistoryEvent;
9use concepts::storage::Locked;
10use concepts::storage::ResponseWithCursor;
11use concepts::storage::Version;
12use concepts::storage::http_client_trace::HttpClientTrace;
13use concepts::{FinishedExecutionFailure, StrVariant};
14use concepts::{FunctionFqn, ParamsParsingError, ResultParsingError};
15use concepts::{Params, SupportedFunctionReturnValue};
16use tracing::Span;
17
18#[async_trait]
19pub trait Worker: Send + Sync + 'static {
20 async fn run(&self, ctx: WorkerContext) -> WorkerResult;
21
22 fn exported_functions_noext(&self) -> &[FunctionMetadata];
25}
26
27pub type WorkerResult = Result<WorkerResultOk, WorkerError>;
28
29#[derive(Debug, derive_more::Display)]
30pub enum WorkerResultOk {
31 #[display("db updated by worker or watcher")]
32 DbUpdatedByWorkerOrWatcher,
33 #[display("{_0}")]
35 RunFinished(RunFinished),
36}
37
38#[derive(Debug, derive_more::Display)]
39#[display("{retval}")]
40pub struct RunFinished {
41 pub retval: SupportedFunctionReturnValue,
42 pub version: Version,
43 pub http_client_traces: Option<Vec<HttpClientTrace>>,
44}
45
46#[derive(Debug)]
47pub struct WorkerContext {
48 pub execution_id: ExecutionId,
49 pub metadata: ExecutionMetadata,
50 pub ffqn: FunctionFqn,
51 pub params: Params,
52 pub event_history: Vec<(HistoryEvent, Version)>,
53 pub responses: Vec<ResponseWithCursor>,
54 pub version: Version,
55 pub can_be_retried: bool,
56 pub worker_span: Span,
57 pub locked_event: Locked,
58 pub executor_close_watcher: tokio::sync::watch::Receiver<bool>,
59}
60
61#[derive(Debug, thiserror::Error)]
62pub enum WorkerError {
63 #[error("activity {trap_kind}: {reason}")]
66 ActivityTrap {
67 reason: String,
68 trap_kind: TrapKind,
69 detail: Option<String>,
70 version: Version,
71 http_client_traces: Option<Vec<HttpClientTrace>>,
72 },
73 #[error("limit reached: {reason}")]
77 LimitReached { reason: String, version: Version },
78 #[error("temporary timeout")]
80 TemporaryTimeout {
81 http_client_traces: Option<Vec<HttpClientTrace>>,
82 version: Version,
83 },
84 #[error("executor closing")]
85 ExecutorClosing(Version),
86 #[error(transparent)]
87 DbError(DbErrorWrite),
88 #[error("fatal error: {0}")]
90 FatalError(FatalError, Version),
91}
92
93#[derive(Debug, thiserror::Error)]
94pub enum FatalError {
95 #[error("nondeterminism detected")]
97 NondeterminismDetected { detail: String },
98 #[error(transparent)]
100 ParamsParsingError(ParamsParsingError),
101 #[error("{reason}")]
103 CannotInstantiate {
104 reason: String,
105 detail: Option<String>,
106 },
107 #[error(transparent)]
109 ResultParsingError(ResultParsingError),
110 #[error("error calling imported function {ffqn} : {reason}")]
112 ImportedFunctionCallError {
113 ffqn: FunctionFqn,
114 reason: StrVariant,
115 detail: Option<String>,
116 },
117 #[error("workflow {trap_kind}: {reason}")]
119 WorkflowTrap {
120 reason: String,
121 trap_kind: TrapKind,
122 detail: Option<String>,
123 },
124 #[error("out of fuel: {reason}")]
125 OutOfFuel { reason: String },
126 #[error("constraint violation: {reason}")]
127 ConstraintViolation { reason: StrVariant },
128 #[error("cancelled")]
130 Cancelled,
131}
132
133impl From<FatalError> for FinishedExecutionFailure {
134 fn from(err: FatalError) -> Self {
135 let reason_generic = err.to_string(); match err {
137 FatalError::NondeterminismDetected { detail } => FinishedExecutionFailure {
138 reason: None,
139 kind: ExecutionFailureKind::NondeterminismDetected,
140 detail: Some(detail),
141 },
142 FatalError::OutOfFuel { reason } => FinishedExecutionFailure {
143 reason: Some(reason),
144 kind: ExecutionFailureKind::OutOfFuel,
145 detail: None,
146 },
147 FatalError::ParamsParsingError(err) => FinishedExecutionFailure {
148 reason: Some(reason_generic),
149 kind: ExecutionFailureKind::Uncategorized,
150 detail: err.detail(),
151 },
152 FatalError::CannotInstantiate { reason, detail } => FinishedExecutionFailure {
153 reason: Some(reason),
154 kind: ExecutionFailureKind::Uncategorized,
155 detail,
156 },
157 FatalError::ResultParsingError(_) | FatalError::ConstraintViolation { reason: _ } => {
158 FinishedExecutionFailure {
159 reason: Some(reason_generic),
160 kind: ExecutionFailureKind::Uncategorized,
161 detail: None,
162 }
163 }
164 FatalError::ImportedFunctionCallError { detail, .. }
165 | FatalError::WorkflowTrap { detail, .. } => FinishedExecutionFailure {
166 reason: Some(reason_generic),
167 kind: ExecutionFailureKind::Uncategorized,
168 detail,
169 },
170 FatalError::Cancelled => FinishedExecutionFailure {
171 kind: ExecutionFailureKind::Cancelled,
172 reason: None,
173 detail: None,
174 },
175 }
176 }
177}
178
179impl From<&FatalError> for FinishedExecutionFailure {
180 fn from(err: &FatalError) -> Self {
181 let reason_generic = err.to_string(); match err {
183 FatalError::NondeterminismDetected { detail } => FinishedExecutionFailure {
184 reason: None,
185 kind: ExecutionFailureKind::NondeterminismDetected,
186 detail: Some(detail.clone()),
187 },
188 FatalError::OutOfFuel { reason } => FinishedExecutionFailure {
189 reason: Some(reason.clone()),
190 kind: ExecutionFailureKind::OutOfFuel,
191 detail: None,
192 },
193 FatalError::ParamsParsingError(err) => FinishedExecutionFailure {
194 reason: Some(reason_generic),
195 kind: ExecutionFailureKind::Uncategorized,
196 detail: err.detail(),
197 },
198 FatalError::CannotInstantiate { reason, detail } => FinishedExecutionFailure {
199 reason: Some(reason.clone()),
200 kind: ExecutionFailureKind::Uncategorized,
201 detail: detail.clone(),
202 },
203 FatalError::ResultParsingError(_) | FatalError::ConstraintViolation { reason: _ } => {
204 FinishedExecutionFailure {
205 reason: Some(reason_generic),
206 kind: ExecutionFailureKind::Uncategorized,
207 detail: None,
208 }
209 }
210 FatalError::ImportedFunctionCallError { detail, .. }
211 | FatalError::WorkflowTrap { detail, .. } => FinishedExecutionFailure {
212 reason: Some(reason_generic),
213 kind: ExecutionFailureKind::Uncategorized,
214 detail: detail.clone(),
215 },
216 FatalError::Cancelled => FinishedExecutionFailure {
217 kind: ExecutionFailureKind::Cancelled,
218 reason: None,
219 detail: None,
220 },
221 }
222 }
223}