use crate::manager::TransactionManager;
use crate::types::{TransactionManagerConfig, TransactionOptions};
use crate::WriteSetAnalyzer;
use linkrs_core::types::VertexId;
fn create_test_manager() -> TransactionManager {
let config = TransactionManagerConfig {
auto_cleanup: false,
..Default::default()
};
TransactionManager::new(config)
}
#[test]
fn test_multiple_non_conflicting_writes() {
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 txn3 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn3");
let ctx1 = manager.get_context(txn1).expect("Failed to get ctx1");
let ctx2 = manager.get_context(txn2).expect("Failed to get ctx2");
let ctx3 = manager.get_context(txn3).expect("Failed to get ctx3");
ctx1.record_vertex_write(VertexId::try_from_int64(1).expect("test vertex id"));
ctx2.record_vertex_write(VertexId::try_from_int64(2).expect("test vertex id"));
ctx3.record_vertex_write(VertexId::try_from_int64(3).expect("test vertex id"));
assert!(manager.check_write_set_conflict(txn1).is_ok());
assert!(manager.check_write_set_conflict(txn2).is_ok());
assert!(manager.check_write_set_conflict(txn3).is_ok());
manager.commit_transaction(txn1).unwrap();
manager.commit_transaction(txn2).unwrap();
manager.commit_transaction(txn3).unwrap();
}
#[test]
fn test_conflicting_writes_same_vertex() {
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 ctx1");
let ctx2 = manager.get_context(txn2).expect("Failed to get ctx2");
let vid = VertexId::try_from_int64(1).expect("test vertex id");
ctx1.record_vertex_write(vid);
ctx2.record_vertex_write(vid);
assert!(manager.check_write_set_conflict(txn1).is_ok());
assert!(manager.check_write_set_conflict(txn2).is_err());
manager.commit_transaction(txn1).unwrap();
assert!(manager.commit_transaction(txn2).is_err());
}
#[test]
fn test_conflict_intensity_varying_overlaps() {
use crate::types::WriteSet;
let ws1 = {
let mut ws = WriteSet::new();
ws.record_vertex(VertexId::try_from_int64(1).expect("test vertex id"));
ws
};
let ws2 = {
let mut ws = WriteSet::new();
ws.record_vertex(VertexId::try_from_int64(2).expect("test vertex id"));
ws
};
assert_eq!(WriteSetAnalyzer::conflict_intensity(&ws1, &ws2), 0.0);
let ws3 = {
let mut ws = WriteSet::new();
ws.record_vertex(VertexId::try_from_int64(1).expect("test vertex id")); ws.record_vertex(VertexId::try_from_int64(3).expect("test vertex id")); ws
};
let intensity = WriteSetAnalyzer::conflict_intensity(&ws1, &ws3);
assert!(intensity > 0.3 && intensity < 0.7);
let ws4 = {
let mut ws = WriteSet::new();
ws.record_vertex(VertexId::try_from_int64(1).expect("test vertex id"));
ws
};
assert_eq!(WriteSetAnalyzer::conflict_intensity(&ws1, &ws4), 1.0);
}
#[test]
fn test_conflict_report_classification() {
use crate::types::WriteSet;
use linkrs_core::types::EdgeIdentifier;
let vid1 = VertexId::try_from_int64(1).expect("test vertex id");
let vid2 = VertexId::try_from_int64(2).expect("test vertex id");
let vid3 = VertexId::try_from_int64(3).expect("test vertex id");
let mut ws_v1 = WriteSet::new();
ws_v1.record_vertex(vid1);
let mut ws_v2 = WriteSet::new();
ws_v2.record_vertex(vid1);
let report_v = WriteSetAnalyzer::analyze_conflict(&ws_v1, &ws_v2);
assert!(report_v.has_conflict);
assert!(report_v.vertex_conflict);
assert!(!report_v.edge_conflict);
let mut ws_e1 = WriteSet::new();
let edge1 = EdgeIdentifier::new(1, vid1, 1, vid2, 1, 0);
ws_e1.record_edge(edge1);
let mut ws_e2 = WriteSet::new();
ws_e2.record_edge(edge1);
let report_e = WriteSetAnalyzer::analyze_conflict(&ws_e1, &ws_e2);
assert!(report_e.has_conflict);
assert!(report_e.edge_conflict);
assert!(!report_e.vertex_conflict);
let mut ws_e3 = WriteSet::new();
let edge2 = EdgeIdentifier::new(1, vid1, 1, vid2, 1, 0);
let edge3 = EdgeIdentifier::new(1, vid1, 1, vid3, 1, 0); ws_e3.record_edge(edge2);
let mut ws_e4 = WriteSet::new();
ws_e4.record_edge(edge3);
let report_shared = WriteSetAnalyzer::analyze_conflict(&ws_e3, &ws_e4);
assert!(!report_shared.has_conflict);
}
#[test]
fn test_readonly_transaction_no_conflict() {
let manager = create_test_manager();
let txn_write = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin write txn");
let txn_read = manager
.begin_read_transaction(TransactionOptions::default())
.expect("Failed to begin read txn");
let ctx_write = manager
.get_context(txn_write)
.expect("Failed to get write ctx");
ctx_write.record_vertex_write(VertexId::try_from_int64(1).expect("test vertex id"));
assert!(manager.check_write_set_conflict(txn_write).is_ok());
assert!(manager.check_write_set_conflict(txn_read).is_ok());
manager.commit_transaction(txn_write).unwrap();
manager.commit_transaction(txn_read).unwrap();
}
#[test]
fn test_empty_write_set_no_conflict() {
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");
assert!(manager.check_write_set_conflict(txn1).is_ok());
assert!(manager.check_write_set_conflict(txn2).is_ok());
manager.commit_transaction(txn1).unwrap();
manager.commit_transaction(txn2).unwrap();
}
#[test]
fn test_write_set_size_tracking() {
let manager = create_test_manager();
let txn = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn");
let ctx = manager.get_context(txn).expect("Failed to get ctx");
assert_eq!(ctx.write_set_size(), 0);
assert!(ctx.is_write_set_empty());
for i in 1..=5 {
ctx.record_vertex_write(VertexId::try_from_int64(i).expect("test vertex id"));
}
assert_eq!(ctx.write_set_size(), 5);
assert!(!ctx.is_write_set_empty());
manager.commit_transaction(txn).unwrap();
}
#[test]
fn test_sequential_conflict_cascade() {
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 txn3 = manager
.begin_insert_transaction(TransactionOptions::default())
.expect("Failed to begin txn3");
let ctx1 = manager.get_context(txn1).expect("Failed to get ctx1");
let ctx2 = manager.get_context(txn2).expect("Failed to get ctx2");
let ctx3 = manager.get_context(txn3).expect("Failed to get ctx3");
let vid = VertexId::try_from_int64(1).expect("test vertex id");
ctx1.record_vertex_write(vid);
ctx2.record_vertex_write(vid);
ctx3.record_vertex_write(vid);
assert!(manager.check_write_set_conflict(txn1).is_ok());
assert!(manager.check_write_set_conflict(txn2).is_err());
assert!(manager.check_write_set_conflict(txn3).is_err());
manager.commit_transaction(txn1).unwrap();
assert!(manager.commit_transaction(txn2).is_err());
assert!(manager.commit_transaction(txn3).is_err());
}