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::{FinishedExecutionError, 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)]
30pub enum WorkerResultOk {
31 DbUpdatedByWorkerOrWatcher,
32 Finished {
33 retval: SupportedFunctionReturnValue,
34 version: Version,
35 http_client_traces: Option<Vec<HttpClientTrace>>,
36 },
37}
38
39#[derive(Debug)]
40pub struct WorkerContext {
41 pub execution_id: ExecutionId,
42 pub metadata: ExecutionMetadata,
43 pub ffqn: FunctionFqn,
44 pub params: Params,
45 pub event_history: Vec<(HistoryEvent, Version)>,
46 pub responses: Vec<ResponseWithCursor>,
47 pub version: Version,
48 pub can_be_retried: bool,
49 pub worker_span: Span,
50 pub locked_event: Locked,
51}
52
53#[derive(Debug, thiserror::Error)]
54pub enum WorkerError {
55 #[error("activity {trap_kind}: {reason}")]
58 ActivityTrap {
59 reason: String,
60 trap_kind: TrapKind,
61 detail: Option<String>,
62 version: Version,
63 http_client_traces: Option<Vec<HttpClientTrace>>,
64 },
65 #[error("{reason}")]
66 ActivityPreopenedDirError {
67 reason: String,
68 detail: String,
69 version: Version,
70 },
71 #[error("activity returned error")]
73 ActivityReturnedError {
74 detail: Option<String>,
75 version: Version,
76 http_client_traces: Option<Vec<HttpClientTrace>>,
77 },
78 #[error("limit reached: {reason}")]
82 LimitReached { reason: String, version: Version },
83 #[error("temporary timeout")]
85 TemporaryTimeout {
86 http_client_traces: Option<Vec<HttpClientTrace>>,
87 version: Version,
88 },
89 #[error(transparent)]
90 DbError(DbErrorWrite),
91 #[error("fatal error: {0}")]
93 FatalError(FatalError, Version),
94}
95
96#[derive(Debug, thiserror::Error)]
97pub enum FatalError {
98 #[error("nondeterminism detected")]
100 NondeterminismDetected { detail: String },
101 #[error(transparent)]
103 ParamsParsingError(ParamsParsingError),
104 #[error("{reason}")]
106 CannotInstantiate {
107 reason: String,
108 detail: Option<String>,
109 },
110 #[error(transparent)]
112 ResultParsingError(ResultParsingError),
113 #[error("error calling imported function {ffqn} : {reason}")]
115 ImportedFunctionCallError {
116 ffqn: FunctionFqn,
117 reason: StrVariant,
118 detail: Option<String>,
119 },
120 #[error("workflow {trap_kind}: {reason}")]
122 WorkflowTrap {
123 reason: String,
124 trap_kind: TrapKind,
125 detail: Option<String>,
126 },
127 #[error("out of fuel: {reason}")]
128 OutOfFuel { reason: String },
129 #[error("constraint violation: {reason}")]
130 ConstraintViolation { reason: StrVariant },
131 #[error("cancelled")]
133 Cancelled,
134}
135
136impl From<FatalError> for FinishedExecutionError {
137 fn from(err: FatalError) -> Self {
138 let reason_generic = err.to_string(); match err {
140 FatalError::NondeterminismDetected { detail } => FinishedExecutionError {
141 reason: None,
142 kind: ExecutionFailureKind::NondeterminismDetected,
143 detail: Some(detail),
144 },
145 FatalError::OutOfFuel { reason } => FinishedExecutionError {
146 reason: Some(reason),
147 kind: ExecutionFailureKind::OutOfFuel,
148 detail: None,
149 },
150 FatalError::ParamsParsingError(err) => FinishedExecutionError {
151 reason: Some(reason_generic),
152 kind: ExecutionFailureKind::Uncategorized,
153 detail: err.detail(),
154 },
155 FatalError::CannotInstantiate { reason, detail } => FinishedExecutionError {
156 reason: Some(reason),
157 kind: ExecutionFailureKind::Uncategorized,
158 detail,
159 },
160 FatalError::ResultParsingError(_) | FatalError::ConstraintViolation { reason: _ } => {
161 FinishedExecutionError {
162 reason: Some(reason_generic),
163 kind: ExecutionFailureKind::Uncategorized,
164 detail: None,
165 }
166 }
167 FatalError::ImportedFunctionCallError { detail, .. }
168 | FatalError::WorkflowTrap { detail, .. } => FinishedExecutionError {
169 reason: Some(reason_generic),
170 kind: ExecutionFailureKind::Uncategorized,
171 detail,
172 },
173 FatalError::Cancelled => FinishedExecutionError {
174 kind: ExecutionFailureKind::Cancelled,
175 reason: None,
176 detail: None,
177 },
178 }
179 }
180}