Skip to main content

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}