1use anyhow::Result;
4
5use crate::ctx::{timestamp_now, WorkflowCtx};
6use crate::store::WorkflowStore;
7use crate::types::*;
8
9impl<S: WorkflowStore> WorkflowCtx<S> {
10 pub async fn schedule_activity(
24 &self,
25 workflow_id: &str,
26 seq: i32,
27 name: &str,
28 input: Option<&str>,
29 task_queue: &str,
30 opts: ScheduleActivityOpts,
31 ) -> Result<WorkflowActivity> {
32 if let Some(existing) = self
34 .store
35 .get_activity_by_workflow_seq(workflow_id, seq)
36 .await?
37 {
38 return Ok(existing);
39 }
40
41 let now = timestamp_now();
42 let mut act = WorkflowActivity {
43 id: None,
44 workflow_id: workflow_id.to_string(),
45 seq,
46 name: name.to_string(),
47 task_queue: task_queue.to_string(),
48 input: input.map(String::from),
49 status: "PENDING".to_string(),
50 result: None,
51 error: None,
52 attempt: 1,
53 max_attempts: opts.max_attempts.unwrap_or(3),
54 initial_interval_secs: opts.initial_interval_secs.unwrap_or(1.0),
55 backoff_coefficient: opts.backoff_coefficient.unwrap_or(2.0),
56 start_to_close_secs: opts.start_to_close_secs.unwrap_or(300.0),
57 heartbeat_timeout_secs: opts.heartbeat_timeout_secs,
58 claimed_by: None,
59 scheduled_at: now,
60 started_at: None,
61 completed_at: None,
62 last_heartbeat: None,
63 };
64
65 let id = self.store.create_activity(&act).await?;
66 act.id = Some(id);
67
68 let event_seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
70 self.store
71 .append_event(&WorkflowEvent {
72 id: None,
73 workflow_id: workflow_id.to_string(),
74 seq: event_seq,
75 event_type: "ActivityScheduled".to_string(),
76 payload: Some(
77 serde_json::json!({
78 "activity_id": id,
79 "activity_seq": seq,
80 "name": name,
81 "task_queue": task_queue,
82 "input": input,
83 })
84 .to_string(),
85 ),
86 timestamp: now,
87 })
88 .await?;
89
90 if let Some(wf) = self.store.get_workflow(workflow_id).await?
92 && wf.status == "PENDING"
93 {
94 self.store
95 .update_workflow_status(workflow_id, WorkflowStatus::Running, None, None)
96 .await?;
97 }
98
99 Ok(act)
100 }
101
102 pub async fn claim_activity(
103 &self,
104 task_queue: &str,
105 worker_id: &str,
106 ) -> Result<Option<WorkflowActivity>> {
107 self.store.claim_activity(task_queue, worker_id).await
108 }
109
110 pub async fn get_activity(&self, id: i64) -> Result<Option<WorkflowActivity>> {
111 self.store.get_activity(id).await
112 }
113
114 pub async fn complete_activity(
122 &self,
123 id: i64,
124 result: Option<&str>,
125 error: Option<&str>,
126 failed: bool,
127 ) -> Result<()> {
128 self.store.complete_activity(id, result, error, failed).await?;
129
130 let act = match self.store.get_activity(id).await? {
132 Some(a) => a,
133 None => return Ok(()),
134 };
135
136 let event_type = if failed {
137 "ActivityFailed"
138 } else {
139 "ActivityCompleted"
140 };
141 let payload = serde_json::json!({
142 "activity_id": id,
143 "activity_seq": act.seq,
144 "name": act.name,
145 "result": result.and_then(|s| serde_json::from_str::<serde_json::Value>(s).ok()),
146 "error": error,
147 });
148 let event_seq = self.store.get_event_count(&act.workflow_id).await? as i32 + 1;
149 let workflow_id = act.workflow_id.clone();
150 self.store
151 .append_event(&WorkflowEvent {
152 id: None,
153 workflow_id: act.workflow_id,
154 seq: event_seq,
155 event_type: event_type.to_string(),
156 payload: Some(payload.to_string()),
157 timestamp: timestamp_now(),
158 })
159 .await?;
160 self.store.mark_workflow_dispatchable(&workflow_id).await?;
163 Ok(())
164 }
165
166 pub async fn fail_activity(&self, id: i64, error: &str) -> Result<()> {
176 let act = match self.store.get_activity(id).await? {
177 Some(a) => a,
178 None => return Ok(()),
179 };
180
181 if act.attempt < act.max_attempts {
182 let backoff = act.initial_interval_secs
184 * act.backoff_coefficient.powi(act.attempt - 1);
185 let next_scheduled_at = timestamp_now() + backoff;
186 self.store
187 .requeue_activity_for_retry(id, act.attempt + 1, next_scheduled_at)
188 .await?;
189 return Ok(());
190 }
191
192 self.store
194 .complete_activity(id, None, Some(error), true)
195 .await?;
196
197 let event_seq = self.store.get_event_count(&act.workflow_id).await? as i32 + 1;
198 let workflow_id = act.workflow_id.clone();
199 self.store
200 .append_event(&WorkflowEvent {
201 id: None,
202 workflow_id: act.workflow_id,
203 seq: event_seq,
204 event_type: "ActivityFailed".to_string(),
205 payload: Some(
206 serde_json::json!({
207 "activity_id": id,
208 "activity_seq": act.seq,
209 "name": act.name,
210 "error": error,
211 "final_attempt": act.attempt,
212 })
213 .to_string(),
214 ),
215 timestamp: timestamp_now(),
216 })
217 .await?;
218 self.store.mark_workflow_dispatchable(&workflow_id).await?;
220 Ok(())
221 }
222
223 pub async fn heartbeat_activity(&self, id: i64, details: Option<&str>) -> Result<()> {
224 self.store.heartbeat_activity(id, details).await
225 }
226
227 pub async fn record_side_effect(
228 &self,
229 workflow_id: &str,
230 value: &str,
231 ) -> Result<()> {
232 let now = timestamp_now();
233 let seq = self.store.get_event_count(workflow_id).await? as i32 + 1;
234 self.store
235 .append_event(&WorkflowEvent {
236 id: None,
237 workflow_id: workflow_id.to_string(),
238 seq,
239 event_type: "SideEffectRecorded".to_string(),
240 payload: Some(value.to_string()),
241 timestamp: now,
242 })
243 .await?;
244 Ok(())
245 }
246}