use std::sync::Arc;
use super::create_test_manager;
use crate::manager::TransactionManager;
use crate::types::*;
use crate::TransactionErrorKind;
#[test]
fn test_check_write_set_conflict_no_conflict() {
use linkrs_core::types::VertexId;
let manager = create_test_manager();
let txn1 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn1");
let txn2 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn2");
let ctx1 = manager.get_context(txn1).expect("Failed to get context 1");
let ctx2 = manager.get_context(txn2).expect("Failed to get context 2");
let vid1 = VertexId::try_from_int64(1).expect("test vertex id");
let vid2 = VertexId::try_from_int64(2).expect("test vertex id");
ctx1.record_vertex_write(vid1);
ctx2.record_vertex_write(vid2);
let conflict_check = manager.check_write_set_conflict(txn1);
assert!(
conflict_check.is_ok(),
"Should be no conflict for different vertices"
);
manager
.commit_transaction(txn1)
.expect("Failed to commit txn1");
manager
.commit_transaction(txn2)
.expect("Failed to commit txn2");
}
#[test]
fn test_check_write_set_conflict_with_conflict() {
use linkrs_core::types::VertexId;
let manager = create_test_manager();
let txn1 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn1");
let txn2 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn2");
let ctx1 = manager.get_context(txn1).expect("Failed to get context 1");
let ctx2 = manager.get_context(txn2).expect("Failed to get context 2");
let vid = VertexId::try_from_int64(1).expect("test vertex id");
ctx1.record_vertex_write(vid);
ctx2.record_vertex_write(vid);
let conflict_check1 = manager.check_write_set_conflict(txn1);
assert!(
conflict_check1.is_ok(),
"First transaction should pass conflict check"
);
let conflict_check2 = manager.check_write_set_conflict(txn2);
assert!(
conflict_check2.is_err(),
"Second transaction should detect conflict with first"
);
manager
.commit_transaction(txn1)
.expect("Failed to commit txn1");
let txn2_error = manager
.commit_transaction(txn2)
.expect_err("Late committer must lose the conflict");
assert_eq!(txn2_error.kind(), TransactionErrorKind::SerializationFailed);
assert!(manager.get_context(txn2).is_err());
}
#[test]
fn test_concurrent_final_review_no_false_abort() {
use std::sync::Barrier;
use std::thread;
use crate::participant::{
TransactionAbortDescriptor, TransactionCommitDescriptor, TransactionCommitSink,
};
use linkrs_core::types::{CommitLsn, VertexId};
struct PassThroughSink {
barrier: Arc<Barrier>,
}
impl TransactionCommitSink for PassThroughSink {
fn commit_transaction(&self, _tid: TransactionId) -> Result<CommitLsn, String> {
Ok(CommitLsn::new(7))
}
fn abort_transaction(&self, _tid: TransactionId) -> Result<(), String> {
Ok(())
}
fn commit_transaction_with_descriptor(
&self,
_descriptor: &TransactionCommitDescriptor,
) -> Result<CommitLsn, String> {
self.barrier.wait();
Ok(CommitLsn::new(7))
}
fn abort_transaction_with_descriptor(
&self,
_descriptor: &TransactionAbortDescriptor,
) -> Result<(), String> {
Ok(())
}
fn finalize_commit(
&self,
_descriptor: &TransactionCommitDescriptor,
_commit_lsn: CommitLsn,
) -> Result<(), String> {
Ok(())
}
fn recover_unfinalized_commits(&self) -> Result<usize, String> {
Ok(0)
}
}
for iteration in 0..20 {
let barrier = Arc::new(Barrier::new(2));
let manager = Arc::new(
TransactionManager::new(TransactionManagerConfig::default()).with_commit_sink(
Arc::new(PassThroughSink {
barrier: Arc::clone(&barrier),
}),
),
);
let vid1 = VertexId::try_from_int64(iteration as i64 * 2).expect("test vertex id");
let vid2 = VertexId::try_from_int64(iteration as i64 * 2 + 1).expect("test vertex id");
let txn1 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("txn1");
let ctx1 = manager.get_context(txn1).expect("ctx1");
ctx1.record_vertex_write(vid1);
drop(ctx1);
let txn2 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("txn2");
let ctx2 = manager.get_context(txn2).expect("ctx2");
ctx2.record_vertex_write(vid2);
drop(ctx2);
let mgr1 = Arc::clone(&manager);
let mgr2 = Arc::clone(&manager);
let h1 = thread::spawn(move || mgr1.commit_transaction(txn1));
let h2 = thread::spawn(move || mgr2.commit_transaction(txn2));
let r1 = h1.join().expect("thread1");
let r2 = h2.join().expect("thread2");
assert!(r1.is_ok(), "non-conflicting txn1 should commit: {:?}", r1);
assert!(r2.is_ok(), "non-conflicting txn2 should commit: {:?}", r2);
}
}