aion_store/package.rs
1//! Deployed-package persistence contract.
2//!
3//! Packages deployed at runtime (the live `Engine::load_package` seam) are
4//! part of the engine's durable state: a run is pinned to the package version
5//! recorded in its `WorkflowStarted` event, so startup recovery can only
6//! resolve that pin if the deployed archive itself survives the restart.
7//! This module defines the store-side contract for that durability — archive
8//! rows keyed by `(workflow_type, content_hash)` plus the per-type route
9//! pointer that decides which version new starts resolve.
10//!
11//! The store treats both values as opaque engine truth: the archive bytes are
12//! the canonical `.aion` container (re-validated by the engine on reload) and
13//! the content hash is its 64-hex textual form. No store backend parses
14//! either.
15
16use async_trait::async_trait;
17use chrono::{DateTime, Utc};
18
19use crate::StoreError;
20
21/// One persisted deployed-package archive.
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct PackageRecord {
24 /// Logical workflow type the package's manifest entry module names.
25 pub workflow_type: String,
26 /// Canonical 64-hex textual content hash identifying this version.
27 pub content_hash: String,
28 /// Complete `.aion` archive bytes as deployed.
29 pub archive: Vec<u8>,
30 /// When this version was (last) deployed.
31 pub deployed_at: DateTime<Utc>,
32}
33
34/// One persisted route pointer: the version new starts of a type resolve.
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct PackageRouteRecord {
37 /// Workflow type the pointer routes.
38 pub workflow_type: String,
39 /// Canonical 64-hex textual content hash the route points at.
40 pub content_hash: String,
41}
42
43/// Durable persistence contract for runtime-deployed workflow packages.
44///
45/// Every Aion event store must implement this: deployed packages are part of
46/// the same durability promise as event history, and a backend that kept
47/// history but dropped packages would strand every recovered run on a
48/// version the catalog cannot resolve.
49#[async_trait]
50pub trait PackageStore: Send + Sync + 'static {
51 /// Persists `record` and atomically points the type's route at it.
52 ///
53 /// This mirrors the engine's load semantics one-to-one: a successful
54 /// load always re-points the route of `record.workflow_type` at
55 /// `record.content_hash`, so the persisted package and the persisted
56 /// route pointer must commit together — a crash between them would
57 /// resurrect a stale route on restart. Re-persisting an existing
58 /// `(workflow_type, content_hash)` replaces the row (idempotent
59 /// re-deploy) and still re-points the route.
60 async fn put_package(&self, record: PackageRecord) -> Result<(), StoreError>;
61
62 /// Lists every persisted package in ascending `deployed_at` order
63 /// (ties broken by `(workflow_type, content_hash)` text order), so
64 /// startup reload re-applies deploys deterministically.
65 async fn list_packages(&self) -> Result<Vec<PackageRecord>, StoreError>;
66
67 /// Deletes the persisted archive for `(workflow_type, content_hash)`.
68 ///
69 /// Deleting an absent row is a no-op, never an error: unload must be
70 /// idempotent and versions loaded from operator-supplied files were
71 /// never persisted.
72 async fn delete_package(
73 &self,
74 workflow_type: &str,
75 content_hash: &str,
76 ) -> Result<(), StoreError>;
77
78 /// Upserts the route pointer for `workflow_type` to `content_hash`.
79 ///
80 /// Used by explicit route re-points (rollback / roll-forward) targeting
81 /// an already-loaded version; the pointed-at version is not required to
82 /// have a persisted archive (it may be an operator-file load), and the
83 /// engine resolves that loudly at reload time.
84 async fn put_package_route(
85 &self,
86 workflow_type: &str,
87 content_hash: &str,
88 ) -> Result<(), StoreError>;
89
90 /// Lists every persisted route pointer in `workflow_type` text order.
91 async fn list_package_routes(&self) -> Result<Vec<PackageRouteRecord>, StoreError>;
92}