sayiir_postgres/
task_result_store.rs1use sayiir_core::codec::{self, Decoder};
9use sayiir_core::snapshot::WorkflowSnapshot;
10use sayiir_persistence::{BackendError, SnapshotStore, TaskResultStore};
11use sqlx::Row;
12
13use crate::backend::PostgresBackend;
14use crate::error::PgError;
15
16impl<C> TaskResultStore for PostgresBackend<C>
17where
18 C: codec::SnapshotCodec,
19{
20 #[tracing::instrument(
21 name = "db.load_task_result",
22 skip(self),
23 fields(db.system = "postgresql"),
24 err(level = tracing::Level::ERROR),
25 )]
26 async fn load_task_result(
27 &self,
28 instance_id: &str,
29 task_id: &sayiir_core::TaskId,
30 ) -> Result<Option<bytes::Bytes>, BackendError> {
31 let snapshot = self.load_snapshot(instance_id).await?;
32
33 if let Some(bytes) = snapshot.get_task_result_bytes(task_id) {
35 return Ok(Some(bytes));
36 }
37
38 if (snapshot.state.is_completed() || snapshot.state.is_failed())
40 && let Some(hist) = self.load_last_in_progress_snapshot(instance_id).await?
41 {
42 return Ok(hist.get_task_result_bytes(task_id));
43 }
44
45 Ok(None)
46 }
47}
48
49impl<C> PostgresBackend<C>
50where
51 C: Decoder + codec::CodecIdentity + codec::sealed::DecodeValue<WorkflowSnapshot>,
52{
53 async fn load_last_in_progress_snapshot(
60 &self,
61 instance_id: &str,
62 ) -> Result<Option<WorkflowSnapshot>, BackendError> {
63 let row = sqlx::query(
64 "SELECT data FROM sayiir_workflow_snapshot_history
65 WHERE instance_id = $1 AND status = 'InProgress'
66 ORDER BY version DESC LIMIT 1",
67 )
68 .bind(instance_id)
69 .fetch_optional(&self.pool)
70 .await
71 .map_err(PgError)?;
72
73 match row {
74 Some(r) => {
75 let raw: &[u8] = r.get("data");
76 let mut snapshot = self.decode(raw)?;
77 let outputs = crate::history::fetch_task_outputs(&self.pool, instance_id).await?;
78 snapshot.hydrate_task_outputs(outputs);
79 Ok(Some(snapshot))
80 }
81 None => Ok(None),
82 }
83 }
84}