1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#![warn(missing_docs)]
use std::{error::Error, fmt, time::Duration};
use async_trait::async_trait;
use sqlx::PgConnection;
use syrup_rail::{BillingEvent, BillingEventSubject};
use crate::host_error::{BoxError, RedactedHostErrorSource};
/// Value-redacted failure returned by the host transaction coordinator.
#[derive(Debug)]
pub struct BillingTransactionError {
source: RedactedHostErrorSource,
}
impl BillingTransactionError {
/// Wraps a host transaction error without exposing its value through
/// ordinary formatting or the standard error-source chain.
pub fn new(source: impl Error + Send + Sync + 'static) -> Self {
Self {
source: RedactedHostErrorSource::new(source),
}
}
/// Returns the host error for explicit application-level inspection.
///
/// Consuming this wrapper is the only boundary that reveals the arbitrary
/// host source.
pub fn into_source(self) -> BoxError {
self.source.into_inner()
}
}
impl fmt::Display for BillingTransactionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("billing transaction operation failed")
}
}
impl Error for BillingTransactionError {}
/// Value-redacted failure returned while appending a host outbox event.
#[derive(Debug)]
pub struct BillingEventWriteError {
source: RedactedHostErrorSource,
}
impl BillingEventWriteError {
/// Wraps a host outbox error without exposing its value through ordinary
/// formatting or the standard error-source chain.
pub fn new(source: impl Error + Send + Sync + 'static) -> Self {
Self {
source: RedactedHostErrorSource::new(source),
}
}
/// Returns the host error for explicit application-level inspection.
///
/// Consuming this wrapper is the only boundary that reveals the arbitrary
/// host source.
pub fn into_source(self) -> BoxError {
self.source.into_inner()
}
}
impl fmt::Display for BillingEventWriteError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("billing event append failed")
}
}
impl Error for BillingEventWriteError {}
/// Durable availability of the host recipient locked for a billing event.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BillingTransactionSubjectState {
/// The host recipient remains live and may receive ordinary mutations.
LiveRecipient,
/// The host retained the billing subject only for financial history.
RetainedSubject,
}
/// Host-prepared transaction whose subject authorization lock is acquired
/// before any shared billing lock.
#[async_trait]
pub trait BillingTransactionCoordinator: Send + Sync {
/// Begins a host transaction and locks the exact event subject before any
/// shared billing row lock is acquired.
async fn begin(
&self,
subject: BillingEventSubject,
lock_timeout: Duration,
) -> Result<Box<dyn BillingTransaction>, BillingTransactionError>;
}
/// One host-owned transaction and its typed event projection capability.
///
/// The returned connection is the same transaction on which `append_event`
/// and `commit` operate. Implementations must not acquire another connection.
#[async_trait]
pub trait BillingTransaction: Send {
/// Returns the connection owned by this exact host transaction.
fn connection(&mut self) -> &mut PgConnection;
/// Returns whether the locked host subject is live or retained.
fn subject_state(&self) -> BillingTransactionSubjectState;
/// Appends a typed billing event to the host outbox on this transaction.
async fn append_event(&mut self, event: &BillingEvent) -> Result<(), BillingEventWriteError>;
/// Commits both host and shared billing changes.
async fn commit(self: Box<Self>) -> Result<(), BillingTransactionError>;
/// Rolls back both host and shared billing changes.
async fn rollback(self: Box<Self>) -> Result<(), BillingTransactionError>;
}