use crate::{Propagation, TransactionStatus};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Transaction {
status: TransactionStatus,
manager_name: String,
propagation: Propagation,
nested: Vec<Transaction>,
}
impl Transaction {
pub fn new(name: impl Into<String>) -> Self {
Self {
status: TransactionStatus::new(name),
manager_name: "default".to_string(),
propagation: Propagation::Required,
nested: Vec::new(),
}
}
pub fn with_status(status: TransactionStatus) -> Self {
Self {
status,
manager_name: "default".to_string(),
propagation: Propagation::Required,
nested: Vec::new(),
}
}
pub fn status(&self) -> &TransactionStatus {
&self.status
}
pub fn status_mut(&mut self) -> &mut TransactionStatus {
&mut self.status
}
pub fn manager_name(&self) -> &str {
&self.manager_name
}
pub fn set_manager_name(&mut self, name: impl Into<String>) {
self.manager_name = name.into();
}
pub fn propagation(&self) -> Propagation {
self.propagation
}
pub fn set_propagation(&mut self, propagation: Propagation) {
self.propagation = propagation;
}
pub fn create_nested(&mut self, name: impl Into<String>) -> Transaction {
let mut nested = Transaction::new(name);
nested.propagation = Propagation::Nested;
nested
}
pub fn nested(&self) -> &[Transaction] {
&self.nested
}
pub fn is_active(&self) -> bool {
!self.status.is_completed()
}
pub fn is_rollback_only(&self) -> bool {
self.status.is_rollback_only()
}
pub fn mark_rollback_only(&self) {
self.status.set_rollback_only();
}
}
pub(crate) struct TransactionHolder {
current: Arc<tokio::sync::RwLock<Option<Transaction>>>,
stack: Arc<tokio::sync::RwLock<Vec<Transaction>>>,
}
impl TransactionHolder {
pub(crate) fn new() -> Self {
Self {
current: Arc::new(tokio::sync::RwLock::new(None)),
stack: Arc::new(tokio::sync::RwLock::new(Vec::new())),
}
}
pub(crate) async fn current(&self) -> Option<Transaction> {
self.current.read().await.clone()
}
pub(crate) async fn set_current(&self, tx: Transaction) {
let mut current = self.current.write().await;
*current = Some(tx);
}
pub(crate) async fn clear(&self) {
let mut current = self.current.write().await;
*current = None;
}
pub(crate) async fn push(&self, tx: Transaction) {
let mut stack = self.stack.write().await;
stack.push(tx);
}
pub(crate) async fn pop(&self) -> Option<Transaction> {
let mut stack = self.stack.write().await;
stack.pop()
}
pub(crate) async fn depth(&self) -> usize {
self.stack.read().await.len()
}
}
impl Default for TransactionHolder {
fn default() -> Self {
Self::new()
}
}
static GLOBAL_HOLDER: std::sync::LazyLock<TransactionHolder> =
std::sync::LazyLock::new(TransactionHolder::new);
pub(crate) fn global_holder() -> &'static TransactionHolder {
&GLOBAL_HOLDER
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_transaction() {
let tx = Transaction::new("test");
assert!(tx.is_active());
assert!(!tx.is_rollback_only());
assert!(tx.status().is_new_transaction());
}
#[tokio::test]
async fn test_transaction_holder() {
let holder = TransactionHolder::new();
let tx = Transaction::new("test");
holder.set_current(tx.clone()).await;
let retrieved = holder.current().await;
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().status().name(), "test");
holder.clear().await;
assert!(holder.current().await.is_none());
}
}