use std::sync::atomic::Ordering;
use std::time::{Duration, Instant};
use super::TransactionContext;
use crate::error::TransactionError;
use crate::types::*;
use crate::wal::Timestamp;
impl TransactionContext {
pub fn get_type(&self) -> TransactionType {
self.txn_type
}
pub fn get_concurrency_mode(&self) -> ConcurrencyMode {
self.concurrency_mode
}
pub fn state(&self) -> TransactionState {
self.state.load()
}
pub fn timestamp(&self) -> Timestamp {
self.start_timestamp
}
pub fn commit_timestamp(&self) -> Timestamp {
self.commit_timestamp.load(Ordering::Relaxed)
}
pub fn set_commit_timestamp(&self, commit_ts: Timestamp) {
self.commit_timestamp.store(commit_ts, Ordering::Relaxed);
}
pub fn is_expired(&self) -> bool {
self.start_time.elapsed() > self.timeout
}
pub fn is_query_timeout(&self) -> bool {
if let Some(query_timeout) = self.query_timeout {
self.statement_start.load().elapsed() > query_timeout
} else {
false
}
}
pub fn is_statement_timeout(&self, statement_start: Instant) -> bool {
if let Some(statement_timeout) = self.statement_timeout {
statement_start.elapsed() > statement_timeout
} else {
false
}
}
pub fn is_idle_timeout(&self) -> bool {
if let Some(idle_timeout) = self.idle_timeout {
self.last_activity.load().elapsed() > idle_timeout
} else {
false
}
}
pub fn check_timeouts(&self) -> Result<(), TransactionError> {
if self.is_expired() {
return Err(TransactionError::transaction_timeout());
}
if self.is_idle_timeout() {
return Err(TransactionError::transaction_timeout());
}
Ok(())
}
pub fn update_activity(&self) {
self.last_activity.store(Instant::now());
}
pub fn begin_statement(&self) -> Result<Instant, TransactionError> {
self.can_execute()?;
self.check_timeouts()?;
let start = Instant::now();
self.statement_start.store(start);
self.increment_query_count();
Ok(start)
}
pub fn finish_statement(&self, statement_start: Instant) -> Result<(), TransactionError> {
let query_timed_out = self
.query_timeout
.is_some_and(|timeout| statement_start.elapsed() > timeout);
let statement_timed_out = self.is_statement_timeout(statement_start);
self.statement_start.store(Instant::now());
self.update_activity();
if query_timed_out || statement_timed_out {
self.mark_rollback_only();
return Err(TransactionError::transaction_timeout());
}
Ok(())
}
pub fn mark_rollback_only(&self) {
self.rollback_only.store(true);
}
pub fn is_rollback_only(&self) -> bool {
self.rollback_only.load()
}
pub fn set_owner(&self, owner: impl Into<String>) {
*self.owner.write() = Some(owner.into());
}
pub fn owner(&self) -> Option<String> {
self.owner.read().clone()
}
pub fn owner_matches(&self, owner: Option<&str>) -> bool {
match (self.owner.read().as_deref(), owner) {
(None, _) => true,
(Some(expected), Some(actual)) => expected == actual,
(Some(_), None) => false,
}
}
pub fn mark_resources_released(&self) -> bool {
self.resources_released
.compare_exchange(false, true)
.is_ok()
}
pub fn resources_released(&self) -> bool {
self.resources_released.load()
}
pub fn remaining_time(&self) -> Duration {
let elapsed = self.start_time.elapsed();
if elapsed >= self.timeout {
Duration::from_secs(0)
} else {
self.timeout - elapsed
}
}
pub fn transition_to(&self, new_state: TransactionState) -> Result<(), TransactionError> {
loop {
let current = self.state.load();
let valid_transition = matches!(
(current, new_state),
(
TransactionState::Active,
TransactionState::Committing | TransactionState::Aborting
) | (
TransactionState::Committing,
TransactionState::Committed
| TransactionState::Aborting
| TransactionState::Aborted
) | (TransactionState::Aborting, TransactionState::Aborted)
);
if !valid_transition {
return Err(TransactionError::invalid_state_transition(
current, new_state,
));
}
if self.state.compare_exchange(current, new_state).is_ok() {
return Ok(());
}
}
}
pub fn validate_write_allowed(&self) -> Result<(), TransactionError> {
if self.read_only {
return Err(TransactionError::read_only_transaction());
}
Ok(())
}
pub fn can_execute(&self) -> Result<(), TransactionError> {
let state = self.state.load();
if !state.can_execute() {
return Err(TransactionError::invalid_state_for_execution(state));
}
if self.is_rollback_only() {
return Err(TransactionError::invalid_state_for_execution(state));
}
if self.is_expired() {
return Err(TransactionError::transaction_expired());
}
Ok(())
}
pub fn info(&self) -> TransactionInfo {
let modified_tables = self.get_modified_tables();
let savepoint_count = self.get_all_savepoints().len();
TransactionInfo {
id: self.id,
state: self.state.load(),
txn_type: self.txn_type,
start_time: self.start_time,
elapsed: self.start_time.elapsed(),
is_read_only: self.read_only,
isolation_level: self.isolation_level,
query_count: self.query_count.load(Ordering::Relaxed),
mutation_count: self.mutation_count.load(Ordering::Relaxed),
modified_tables,
savepoint_count,
read_timestamp: self.effective_read_timestamp(),
write_timestamp: if self.read_only { 0 } else { self.timestamp() },
owner: self.owner(),
last_activity: self.last_activity.load().elapsed(),
rollback_only: self.is_rollback_only(),
blocking_reason: if self.is_rollback_only() {
Some("transaction is marked rollback-only".to_string())
} else {
None
},
staged_bytes: self.staged_bytes(),
undo_bytes: self.undo_log_len() as u64,
}
}
pub fn clear(&self) -> Result<(), TransactionError> {
self.clear_undo_logs()?;
{
let mut write_set = self.write_set.lock();
*write_set = WriteSet::new();
}
self.write_validated.store(false);
self.rollback_only.store(false);
self.resources_released.store(false);
self.staged_bytes.store(0, Ordering::Relaxed);
{
let mut tables = self.modified_tables.lock();
tables.clear();
}
{
let mut manager = self.savepoint_manager.write();
manager.clear();
}
self.mutation_journal.write().truncate(0);
self.local_wal.lock().clear();
self.restore_read_set(WriteSet::new());
self.mutation_count.store(0, Ordering::Relaxed);
self.undo_bytes.store(0, Ordering::Relaxed);
Ok(())
}
}