#![cfg(feature = "pubsub")]
use core::ptr::NonNull;
use remdb::pubsub::topics::*;
use remdb::pubsub::{PubSub, PubSubConfig, UdpMode};
use remdb::time_series::compression::CompressionType;
use remdb::time_series::table::TimeSeriesConfig;
use remdb::{
config::{DbConfig, MemoryAllocator, WALConfig},
RemDb,
};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
struct SimpleAllocator;
impl MemoryAllocator for SimpleAllocator {
fn allocate(&self, _size: usize) -> Option<NonNull<u8>> {
static mut BUFFER: [u8; 4 * 1024 * 1024] = [0u8; 4 * 1024 * 1024];
unsafe { Some(NonNull::new(core::ptr::addr_of_mut!(BUFFER).cast::<u8>()).unwrap()) }
}
fn deallocate(&self, _ptr: NonNull<u8>, _size: usize) {
}
}
unsafe impl Sync for SimpleAllocator {}
static ALLOCATOR: SimpleAllocator = SimpleAllocator;
const WAL_TOPIC_ID: u16 = 1;
const TABLES_TOPIC_ID: u16 = 9;
const METRICS_TOPIC_ID: u16 = 10;
const HEALTH_STATUS_TOPIC_ID: u16 = 11;
fn main() {
println!("Starting PubSub SQL Test Server...");
let mut mem_buffer = Box::new([0u8; 4 * 1024 * 1024]);
let ptr = mem_buffer.as_mut_ptr();
remdb::memory::allocator::init_global_allocator(ptr, mem_buffer.len())
.expect("Failed to initialize global allocator");
static CONFIG: DbConfig = DbConfig {
tables: vec![], total_memory: 4 * 1024 * 1024, low_power_mode_supported: false,
low_power_max_records: None,
default_max_records: 10000,
memory_allocator: &ALLOCATOR, wal_config: WALConfig {
log_path: "./wal",
log_mode: remdb::config::LogMode::Async,
checkpoint_interval_ms: 60000,
log_file_size_limit: 16 * 1024 * 1024,
log_prealloc_size: 4 * 1024 * 1024,
log_segment_size: 16 * 1024 * 1024,
retained_checkpoints: 2,
max_consecutive_invalid: 100,
skip_threshold: 1000,
skip_block_size: 1024 * 1024,
max_skip_attempts: 3,
compression_type: remdb::config::WALCompressionType::None,
compression_level: 3,
},
time_series_defaults: TimeSeriesConfig {
partition_duration_secs: 3600, retention_period_secs: 7 * 24 * 3600, compression: CompressionType::None,
max_partitions: 100,
},
#[cfg(feature = "pubsub")]
pubsub_config: None,
#[cfg(feature = "ha")]
ha_config: Some(remdb::config::HAConfig {
node_id: 1, ha_role: remdb::ha::HARole::Auto,
replication_mode: remdb::ha::ReplicationMode::Async,
heartbeat_interval_ms: 5000, failure_detection_ms: 15000, sync_timeout_ms: 5000, master_address: None,
master_port: None,
replication_port: 5556,
}),
model_worker_config: remdb::config::ModelWorkerConfig::DEFAULT,
};
let mut db = RemDb::new(&CONFIG);
db.init().expect("Failed to initialize database");
let db_shared = Arc::new(Mutex::new(db));
let config = PubSubConfig {
udp_mode: UdpMode::Broadcast,
multicast_addr: None,
port: 5555,
max_topics: 32,
max_subscribers_per_topic: 16,
buffer_size: 4096,
enable_nack: true,
retransmit_timeout: Duration::from_millis(100),
max_retransmits: 3,
heartbeat_interval: Duration::from_secs(5),
frame_pool_size: 128,
};
let mut pubsub = PubSub::new(config).expect("Failed to create PubSub instance");
pubsub.init().expect("Failed to initialize PubSub");
pubsub
.register_topic(WAL_TOPIC, WAL_TOPIC_ID)
.expect("Failed to register WAL topic");
pubsub
.register_topic(TABLES_TOPIC, TABLES_TOPIC_ID)
.expect("Failed to register tables topic");
pubsub
.register_topic(METRICS_TOPIC, METRICS_TOPIC_ID)
.expect("Failed to register metrics topic");
pubsub
.register_topic(HEALTH_STATUS_TOPIC, HEALTH_STATUS_TOPIC_ID)
.expect("Failed to register health status topic");
println!("PubSub test server started successfully!");
println!("Listening on UDP port 5555");
println!("Topics available:");
println!("- WAL (ID: {}) - All WAL operations", WAL_TOPIC_ID);
println!(
"- TABLES (ID: {}) - Table creation/deletion events",
TABLES_TOPIC_ID
);
println!("- METRICS (ID: {}) - Database metrics", METRICS_TOPIC_ID);
println!(
"- HEALTH_STATUS (ID: {}) - Health status updates",
HEALTH_STATUS_TOPIC_ID
);
println!("- HEARTBEAT - Sent every 5 seconds");
let pubsub_clone = Arc::new(Mutex::new(pubsub));
let running = Arc::new(Mutex::new(true));
let running_clone = running.clone();
let server_clone = pubsub_clone.clone();
let _heartbeat_thread = thread::spawn(move || {
let interval = Duration::from_secs(5);
while *running_clone.lock().unwrap() {
match server_clone.lock().unwrap().publish(0, b"heartbeat") {
Ok(_) => println!("Heartbeat sent"),
Err(e) => println!("Failed to send heartbeat: {:?}", e),
}
thread::sleep(interval);
}
});
let db_clone = db_shared.clone();
let server_clone_sql = pubsub_clone.clone();
let running_clone_sql = running.clone();
let _sql_thread = thread::spawn(move || {
let server_clone = server_clone_sql;
let db = db_clone;
println!("Starting SQL operations...");
let create_table_sql = "CREATE TABLE users (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n age INTEGER,\n email TEXT UNIQUE,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )";
match db.lock().unwrap().sql_query(create_table_sql) {
Ok(result) => println!("Created table users: {}", result.to_string()),
Err(e) => println!("Failed to create table: {:?}", e),
}
let users_table_topic = format!("table.{}", "users");
let table_topic_id = 12;
let insert_sql =
"INSERT INTO users (name, age, email) VALUES ('Alice', 25, 'alice@example.com')";
match db.lock().unwrap().sql_query(insert_sql) {
Ok(result) => {
println!("Inserted record: {}", result.to_string());
let table_data = format!("INSERT: Table=users, Record=Alice");
match server_clone
.lock()
.unwrap()
.publish(table_topic_id, table_data.as_bytes())
{
Ok(_) => println!("Published {}: {}", users_table_topic, table_data),
Err(e) => println!("Failed to publish {}: {:?}", users_table_topic, e),
}
}
Err(e) => println!("Failed to insert record: {:?}", e),
}
thread::sleep(Duration::from_secs(1));
let insert_sql =
"INSERT INTO users (name, age, email) VALUES ('Bob', 30, 'bob@example.com')";
match db.lock().unwrap().sql_query(insert_sql) {
Ok(result) => {
println!("Inserted record: {}", result.to_string());
let table_data = format!("INSERT: Table=users, Record=Bob");
match server_clone
.lock()
.unwrap()
.publish(table_topic_id, table_data.as_bytes())
{
Ok(_) => println!("Published {}: {}", users_table_topic, table_data),
Err(e) => println!("Failed to publish {}: {:?}", users_table_topic, e),
}
}
Err(e) => println!("Failed to insert record: {:?}", e),
}
thread::sleep(Duration::from_secs(1));
let update_sql =
"UPDATE users SET age = 31, email = 'robert@example.com' WHERE name = 'Bob'";
match db.lock().unwrap().sql_query(update_sql) {
Ok(result) => {
println!("Updated record: {}", result.to_string());
let table_data = format!("UPDATE: Table=users, Record=Bob");
match server_clone
.lock()
.unwrap()
.publish(table_topic_id, table_data.as_bytes())
{
Ok(_) => println!("Published {}: {}", users_table_topic, table_data),
Err(e) => println!("Failed to publish {}: {:?}", users_table_topic, e),
}
}
Err(e) => println!("Failed to update record: {:?}", e),
}
thread::sleep(Duration::from_secs(1));
let delete_sql = "DELETE FROM users WHERE name = 'Alice'";
match db.lock().unwrap().sql_query(delete_sql) {
Ok(result) => {
println!("Deleted record: {}", result.to_string());
let table_data = format!("DELETE: Table=users, Record=Alice");
match server_clone
.lock()
.unwrap()
.publish(table_topic_id, table_data.as_bytes())
{
Ok(_) => println!("Published {}: {}", users_table_topic, table_data),
Err(e) => println!("Failed to publish {}: {:?}", users_table_topic, e),
}
}
Err(e) => println!("Failed to delete record: {:?}", e),
}
thread::sleep(Duration::from_secs(1));
let select_sql = "SELECT * FROM users";
match db.lock().unwrap().sql_query(select_sql) {
Ok(result) => println!("Query result: {}", result.to_string()),
Err(e) => println!("Failed to query records: {:?}", e),
}
while *running_clone_sql.lock().unwrap() {
thread::sleep(Duration::from_secs(1));
}
});
let running_clone_metrics = running.clone();
let server_clone_metrics = pubsub_clone.clone();
let db_metrics_clone = db_shared.clone();
let _metrics_thread = thread::spawn(move || {
let interval = Duration::from_millis(4000);
let db = db_metrics_clone;
while *running_clone_metrics.lock().unwrap() {
let metrics = db.lock().unwrap().metrics.snapshot();
let metrics_data = metrics.to_json();
match server_clone_metrics
.lock()
.unwrap()
.publish(METRICS_TOPIC_ID, metrics_data.as_bytes())
{
Ok(_) => println!("Published METRICS: {}", metrics_data),
Err(e) => println!("Failed to publish METRICS: {:?}", e),
}
thread::sleep(interval);
}
});
let running_clone_health = running.clone();
let server_clone_health = pubsub_clone.clone();
let db_health_clone = db_shared.clone();
let _health_thread = thread::spawn(move || {
let interval = Duration::from_millis(5000);
let db = db_health_clone;
while *running_clone_health.lock().unwrap() {
let health = db.lock().unwrap().health_check();
let health_data = health.to_json();
match server_clone_health
.lock()
.unwrap()
.publish(HEALTH_STATUS_TOPIC_ID, health_data.as_bytes())
{
Ok(_) => println!("Published HEALTH_STATUS: {}", health_data.trim()),
Err(e) => println!("Failed to publish HEALTH_STATUS: {:?}", e),
}
thread::sleep(interval);
}
});
thread::sleep(Duration::from_secs(60));
*running.lock().unwrap() = false;
println!("PubSub SQL test server stopped!");
}