use remdb::config::{
DbConfig, DefaultMemoryAllocator, LogMode, TimeSeriesConfig, WALCompressionType, WALConfig,
};
use remdb::platform::{init_platform, FileHandle, FileMode, FileResult, Platform, SeekWhence};
use remdb::transaction::{LogItem, LogManager, LogOperation, VariableSizeLogItem};
mod common;
use common::setup_test_db_with_posix;
#[cfg(windows)]
fn get_test_wal_path(name: &str) -> &'static str {
let s = format!("C:\\temp\\{}", name);
Box::leak(s.into_boxed_str())
}
#[cfg(not(windows))]
fn get_test_wal_path(name: &str) -> &'static str {
let s = format!("/tmp/{}", name);
Box::leak(s.into_boxed_str())
}
#[test]
fn test_variable_size_log_item_write_and_read() {
setup_test_db_with_posix();
static ALLOCATOR: DefaultMemoryAllocator = DefaultMemoryAllocator;
let config = DbConfig {
tables: vec![],
total_memory: 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &ALLOCATOR,
wal_config: WALConfig {
log_path: &get_test_wal_path("test_variable_size"),
log_mode: LogMode::Sync,
checkpoint_interval_ms: 60000,
log_file_size_limit: 16 * 1024 * 1024,
log_prealloc_size: 1 * 1024 * 1024,
log_segment_size: 16 * 1024 * 1024,
retained_checkpoints: 3,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: None,
model_worker_config: Default::default(),
};
unsafe {
let mut log_manager = LogManager::new(&config).unwrap();
let small_new_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8];
let mut small_log_item = VariableSizeLogItem {
header: LogItem {
op_type: LogOperation::Insert,
table_id: 0,
record_id: 1,
old_data_size: 0,
new_data_size: small_new_data.len() as u16,
tx_id: 1,
timestamp: 1234567890,
checksum: 0,
},
old_data: vec![],
new_data: small_new_data,
};
let calculated_checksum = unsafe {
remdb::transaction::Transaction::calculate_variable_size_log_item_checksum(
&small_log_item,
)
};
small_log_item.header.checksum = calculated_checksum;
let result = log_manager.write_variable_size_log_item(&small_log_item);
assert!(
result.is_ok(),
"Failed to write small variable size log item"
);
let mut large_new_data = vec![0u8; 1024];
for i in 0..1024 {
large_new_data[i] = (i % 256) as u8;
}
let mut large_log_item = VariableSizeLogItem {
header: LogItem {
op_type: LogOperation::Insert,
table_id: 0,
record_id: 2,
old_data_size: 0,
new_data_size: large_new_data.len() as u16,
tx_id: 2,
timestamp: 1234567891,
checksum: 0,
},
old_data: vec![],
new_data: large_new_data,
};
let calculated_checksum = unsafe {
remdb::transaction::Transaction::calculate_variable_size_log_item_checksum(
&large_log_item,
)
};
large_log_item.header.checksum = calculated_checksum;
let result = log_manager.write_variable_size_log_item(&large_log_item);
assert!(
result.is_ok(),
"Failed to write large variable size log item"
);
let old_data = vec![1u8, 2, 3, 4];
let new_data = vec![5u8, 6, 7, 8];
let mut update_log_item = VariableSizeLogItem {
header: LogItem {
op_type: LogOperation::Update,
table_id: 0,
record_id: 1,
old_data_size: old_data.len() as u16,
new_data_size: new_data.len() as u16,
tx_id: 3,
timestamp: 1234567892,
checksum: 0,
},
old_data,
new_data,
};
let calculated_checksum = unsafe {
remdb::transaction::Transaction::calculate_variable_size_log_item_checksum(
&update_log_item,
)
};
update_log_item.header.checksum = calculated_checksum;
let result = log_manager.write_variable_size_log_item(&update_log_item);
assert!(
result.is_ok(),
"Failed to write update variable size log item"
);
let read_small = log_manager.read_variable_size_log_item(0);
assert!(
read_small.is_ok(),
"Failed to read small variable size log item"
);
let read_small = read_small.unwrap();
assert_eq!(read_small.header.op_type, LogOperation::Insert);
assert_eq!(read_small.header.table_id, 0);
assert_eq!(read_small.header.record_id, 1);
assert_eq!(read_small.header.new_data_size, 8);
assert_eq!(read_small.new_data.len(), 8);
assert_eq!(read_small.new_data, vec![1u8, 2, 3, 4, 5, 6, 7, 8]);
let read_large = log_manager.read_variable_size_log_item(1);
assert!(
read_large.is_ok(),
"Failed to read large variable size log item"
);
let read_large = read_large.unwrap();
assert_eq!(read_large.header.op_type, LogOperation::Insert);
assert_eq!(read_large.header.table_id, 0);
assert_eq!(read_large.header.record_id, 2);
assert_eq!(read_large.header.new_data_size, 1024);
assert_eq!(read_large.new_data.len(), 1024);
for i in 0..1024 {
assert_eq!(read_large.new_data[i], (i % 256) as u8);
}
let read_update = log_manager.read_variable_size_log_item(2);
assert!(
read_update.is_ok(),
"Failed to read update variable size log item"
);
let read_update = read_update.unwrap();
assert_eq!(read_update.header.op_type, LogOperation::Update);
assert_eq!(read_update.header.table_id, 0);
assert_eq!(read_update.header.record_id, 1);
assert_eq!(read_update.header.old_data_size, 4);
assert_eq!(read_update.header.new_data_size, 4);
assert_eq!(read_update.old_data, vec![1u8, 2, 3, 4]);
assert_eq!(read_update.new_data, vec![5u8, 6, 7, 8]);
println!("✅ 可变大小日志项写入和读取测试通过!");
}
}
#[test]
fn test_variable_size_log_item_large_record() {
setup_test_db_with_posix();
static ALLOCATOR: DefaultMemoryAllocator = DefaultMemoryAllocator;
let config = DbConfig {
tables: vec![],
total_memory: 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 1000,
memory_allocator: &ALLOCATOR,
wal_config: WALConfig {
log_path: &get_test_wal_path("test_large_record"),
log_mode: LogMode::Sync,
checkpoint_interval_ms: 60000,
log_file_size_limit: 16 * 1024 * 1024,
log_prealloc_size: 1 * 1024 * 1024,
log_segment_size: 16 * 1024 * 1024,
retained_checkpoints: 3,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: TimeSeriesConfig::DEFAULT,
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: None,
model_worker_config: Default::default(),
};
unsafe {
let mut log_manager = LogManager::new(&config).unwrap();
let mut large_data = Vec::with_capacity(2048);
for i in 0..2048 {
large_data.push((i % 256) as u8);
}
let mut large_log_item = VariableSizeLogItem {
header: LogItem {
op_type: LogOperation::Insert,
table_id: 0,
record_id: 1,
old_data_size: 0,
new_data_size: large_data.len() as u16,
tx_id: 1,
timestamp: 1234567890,
checksum: 0,
},
old_data: vec![],
new_data: large_data,
};
let calculated_checksum = unsafe {
remdb::transaction::Transaction::calculate_variable_size_log_item_checksum(
&large_log_item,
)
};
large_log_item.header.checksum = calculated_checksum;
let result = log_manager.write_variable_size_log_item(&large_log_item);
assert!(result.is_ok(), "Failed to write large record log item");
let read_large = log_manager.read_variable_size_log_item(0);
assert!(read_large.is_ok(), "Failed to read large record log item");
let read_large = read_large.unwrap();
assert_eq!(read_large.header.new_data_size, 2048);
assert_eq!(read_large.new_data.len(), 2048);
for i in 0..2048 {
assert_eq!(read_large.new_data[i], (i % 256) as u8);
}
println!("✅ 超大型记录测试通过!");
}
}
#[test]
fn test_variable_size_log_item_checksum() {
let new_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8];
let mut log_item = VariableSizeLogItem {
header: LogItem {
op_type: LogOperation::Insert,
table_id: 0,
record_id: 1,
old_data_size: 0,
new_data_size: new_data.len() as u16,
tx_id: 1,
timestamp: 1234567890,
checksum: 0,
},
old_data: vec![],
new_data: new_data.clone(),
};
let calculated_checksum = unsafe {
remdb::transaction::Transaction::calculate_variable_size_log_item_checksum(&log_item)
};
log_item.header.checksum = calculated_checksum;
let recalculated_checksum = unsafe {
remdb::transaction::Transaction::calculate_variable_size_log_item_checksum(&log_item)
};
assert_eq!(log_item.header.checksum, recalculated_checksum);
println!("✅ 校验和计算测试通过!");
}