mod common;
use linkrs_core::types::VertexId;
use linkrs_core::Value;
use linkrs_storage::{StorageReader, StorageWriter};
use std::sync::{Arc, Barrier, Mutex};
use std::thread;
#[test]
fn test_concurrent_vertex_insertion() {
let mut storage = common::create_in_memory_storage();
common::setup_basic_schema(&mut storage);
let storage = Arc::new(Mutex::new(storage));
let barrier = Arc::new(Barrier::new(8));
let mut handles = vec![];
for thread_id in 0..8 {
let storage = Arc::clone(&storage);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait();
for op in 0..100 {
let vid = VertexId::try_from_int64((thread_id * 100 + op) as i64 + 1000)
.expect("test vertex id");
let name = if let Some(id) = vid.as_int64() {
format!("Person_{}", id)
} else {
format!("Person_{}", op)
};
let vertex = common::create_person_vertex(
vid.as_int64().unwrap_or(op as i64 + 1000),
&name,
20,
);
let mut st = storage.lock().unwrap();
st.insert_vertex("test_space", vertex)
.expect("concurrent insert failed");
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
let st = storage.lock().unwrap();
let vertices = st.scan_vertices("test_space").unwrap();
assert_eq!(
vertices.len(),
800,
"Expected 800 vertices from 8 threads * 100 ops"
);
}
#[test]
fn test_concurrent_read_write_consistency() {
let mut storage = common::create_in_memory_storage();
common::setup_basic_schema(&mut storage);
for i in 0..10 {
let v = common::create_person_vertex(i, &format!("Person{}", i), 20);
storage.insert_vertex("test_space", v).unwrap();
}
let storage = Arc::new(Mutex::new(storage));
let barrier = Arc::new(Barrier::new(5));
let mut handles = vec![];
for thread_id in 0..5 {
let storage = Arc::clone(&storage);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait();
if thread_id == 4 {
for i in 10..15 {
let v = common::create_person_vertex(i, &format!("Person{}", i), 25);
let mut st = storage.lock().unwrap();
st.insert_vertex("test_space", v).unwrap();
}
} else {
for _ in 0..50 {
let st = storage.lock().unwrap();
let vertices = st.scan_vertices("test_space").unwrap();
assert!(vertices.len() >= 10, "Should have at least 10 vertices");
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
}
#[test]
fn test_concurrent_edge_creation_same_target() {
let mut storage = common::create_in_memory_storage();
common::setup_basic_schema(&mut storage);
for i in 0..20 {
let v = common::create_person_vertex(i, &format!("Person{}", i), 20);
storage.insert_vertex("test_space", v).unwrap();
}
let storage = Arc::new(Mutex::new(storage));
let barrier = Arc::new(Barrier::new(8));
let mut handles = vec![];
for thread_id in 0..8 {
let storage = Arc::clone(&storage);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait();
for edge_idx in 0..10 {
let src_vid = (thread_id * 10 + edge_idx) as i64 + 2;
let dst_vid = 1i64;
let edge = common::create_knows_edge(src_vid, dst_vid, 2020 + edge_idx);
let mut st = storage.lock().unwrap();
let result = st.insert_edge("test_space", edge);
let _ = result;
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
let st = storage.lock().unwrap();
let all_vertices = st.scan_vertices("test_space").unwrap();
assert!(
!all_vertices.is_empty(),
"Should have vertices after concurrent operations"
);
}
#[test]
fn test_concurrent_index_lookup_during_insert() {
let mut storage = common::create_in_memory_storage();
common::setup_basic_schema(&mut storage);
common::create_person_name_index(&mut storage, "test_space");
let storage = Arc::new(Mutex::new(storage));
let barrier = Arc::new(Barrier::new(2));
let mut handles = vec![];
{
let storage = Arc::clone(&storage);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait();
for i in 0..50 {
let v = common::create_person_vertex(i, &format!("Person{}", i), 20);
let mut st = storage.lock().unwrap();
st.insert_vertex("test_space", v).unwrap();
}
});
handles.push(handle);
}
{
let storage = Arc::clone(&storage);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait();
for i in 0..25 {
let st = storage.lock().unwrap();
let result = st.lookup_index(
"test_space",
"person_name_idx",
&Value::string(format!("Person{}", i)),
);
let _ = result;
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
}
#[test]
fn test_concurrent_vertex_updates_consistency() {
let mut storage = common::create_in_memory_storage();
common::setup_basic_schema(&mut storage);
for i in 0..10 {
let v = common::create_person_vertex(i, &format!("Person{}", i), 20);
storage.insert_vertex("test_space", v).unwrap();
}
let storage = Arc::new(Mutex::new(storage));
let barrier = Arc::new(Barrier::new(4));
let mut handles = vec![];
for thread_id in 0..4 {
let storage = Arc::clone(&storage);
let barrier = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier.wait();
for vertex_idx in (thread_id * 2)..(thread_id * 2 + 2) {
if vertex_idx < 10 {
let vid = VertexId::try_from_int64(vertex_idx as i64).expect("test vertex id");
let st = storage.lock().unwrap();
if let Some(v) = st.get_vertex("test_space", "Person", &vid).unwrap() {
assert!(
!v.tag.properties.is_empty(),
"Vertex should have properties"
);
assert!(
v.properties().contains_key("name"),
"Vertex should have name property"
);
}
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread panicked");
}
let st = storage.lock().unwrap();
let vertices = st.scan_vertices("test_space").unwrap();
assert_eq!(vertices.len(), 10);
}