Files
foxhunt/crates/trading_engine/src/persistence/mod.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt,
consolidate 10 test crates into testing/, split config crate
from deployment config files.

Root directory reduced from 38+ to ~17 directories.
All Cargo.toml paths and build.rs proto refs updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:56:00 +01:00

268 lines
9.7 KiB
Rust

//! Core Persistence Layer for Foxhunt HFT Trading System
//!
//! This module provides the main database connectivity and data persistence
//! infrastructure for high-frequency trading operations.
//!
//! # Architecture
//!
//! ``text
//! ┌─────────────────────────────────────────────────────────────────┐
//! │ Foxhunt Persistence Stack │
//! ├─────────────────────────────────────────────────────────────────┤
//! │ Trading Layer: Order Management, Position Tracking │
//! ├─────────────────────────────────────────────────────────────────┤
//! │ Persistence Layer: PostgreSQL, InfluxDB, Redis, ClickHouse │
//! ├─────────────────────────────────────────────────────────────────┤
//! │ Connection Management: Pools, Health Checks, Failover │
//! ├─────────────────────────────────────────────────────────────────┤
//! │ Performance Layer: Sub-1ms timeouts, Connection prewarming │
//! └─────────────────────────────────────────────────────────────────┘
//! ``
pub mod backup;
pub mod clickhouse;
pub mod health;
pub mod influxdb;
pub mod migrations;
pub mod postgres;
pub mod redis;
#[cfg(test)]
mod redis_integration_test;
// DO NOT RE-EXPORT - Use explicit imports at usage sites
use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error;
// Import required config types from submodules
use crate::persistence::backup::create_full_backup;
use crate::persistence::clickhouse::{ClickHouseClient, ClickHouseConfig, ClickHouseError};
use crate::persistence::health::{HealthStatus, PersistenceHealth};
use crate::persistence::influxdb::{InfluxClient, InfluxConfig, InfluxError};
use crate::persistence::migrations::run_pending_migrations;
use crate::persistence::postgres::{PostgresConfig, PostgresError, PostgresPool};
use crate::persistence::redis::{RedisConfig, RedisError, RedisPool};
/// Core persistence configuration for all database systems
#[derive(Debug, Clone, Deserialize, Serialize)]
/// PersistenceConfig
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct PersistenceConfig {
/// `PostgreSQL` configuration for main trading data
pub postgres: PostgresConfig,
/// `InfluxDB` configuration for time-series metrics
pub influx: InfluxConfig,
/// `Redis` configuration for caching and session data
pub redis: RedisConfig,
/// `ClickHouse` configuration for analytics (optional)
pub clickhouse: Option<ClickHouseConfig>,
/// Global persistence settings
pub global: GlobalPersistenceConfig,
}
/// Global persistence settings affecting all database connections
#[derive(Debug, Clone, Deserialize, Serialize)]
/// GlobalPersistenceConfig
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct GlobalPersistenceConfig {
/// Environment (development, staging, production)
pub environment: String,
/// Enable detailed query logging for performance analysis
pub enable_query_logging: bool,
/// Enable connection pool monitoring
pub enable_pool_monitoring: bool,
/// Enable automatic health checks
pub enable_health_checks: bool,
/// Health check interval in seconds
pub health_check_interval_seconds: u64,
/// Maximum allowed query latency in microseconds for `HFT` operations
pub max_query_latency_micros: u64,
}
impl Default for GlobalPersistenceConfig {
fn default() -> Self {
Self {
environment: "development".to_owned(),
enable_query_logging: true,
enable_pool_monitoring: true,
enable_health_checks: true,
health_check_interval_seconds: 30,
max_query_latency_micros: 800, // <1ms for HFT
}
}
}
/// Unified error type for all persistence operations
#[derive(Debug, Error)]
/// PersistenceError
///
/// Auto-generated documentation placeholder - enhance with specifics
pub enum PersistenceError {
#[error("PostgreSQL error: {0}")]
// Postgres variant
Postgres(#[from] PostgresError),
#[error("InfluxDB error: {0}")]
// Influx variant
Influx(#[from] InfluxError),
#[error("Redis error: {0}")]
// Redis variant
Redis(#[from] RedisError),
#[error("ClickHouse error: {0}")]
// ClickHouse variant
ClickHouse(#[from] ClickHouseError),
#[error("Configuration error: {0}")]
// Configuration variant
Configuration(String),
#[error("Health check failed: {0}")]
// HealthCheck variant
HealthCheck(String),
#[error(
"Performance violation: {operation} took {actual_micros}\u{3bc}s, max allowed {max_micros}\u{3bc}s"
)]
PerformanceViolation {
operation: String,
actual_micros: u64,
max_micros: u64,
},
}
/// Main persistence manager coordinating all database connections
#[derive(Debug)]
pub struct PersistenceManager {
postgres: PostgresPool,
influx: InfluxClient,
redis: RedisPool,
clickhouse: Option<ClickHouseClient>,
config: PersistenceConfig,
health: PersistenceHealth,
}
impl PersistenceManager {
/// Initialize the persistence manager with all database connections
pub async fn new(config: PersistenceConfig) -> Result<Self, PersistenceError> {
// Initialize PostgreSQL connection pool for main trading data
let postgres = PostgresPool::new(config.postgres.clone()).await?;
// Initialize InfluxDB client for time-series data
let influx = InfluxClient::new(config.influx.clone()).await?;
// Initialize Redis connection pool for caching
let redis = RedisPool::new(config.redis.clone()).await?;
// Initialize ClickHouse client if configured
let clickhouse = if let Some(ch_config) = &config.clickhouse {
Some(ClickHouseClient::new(ch_config.clone()).await?)
} else {
// None variant
None
};
// Initialize health monitoring
let health = PersistenceHealth::new(
config.global.enable_health_checks,
Duration::from_secs(config.global.health_check_interval_seconds),
);
Ok(Self {
postgres,
influx,
redis,
clickhouse,
config,
health,
})
}
/// Get `PostgreSQL` connection pool
pub const fn postgres(&self) -> &PostgresPool {
&self.postgres
}
/// Get `InfluxDB` client
pub const fn influx(&self) -> &InfluxClient {
&self.influx
}
/// Get `Redis` connection pool
pub const fn redis(&self) -> &RedisPool {
&self.redis
}
/// Get `ClickHouse` client (if configured)
pub const fn clickhouse(&self) -> Option<&ClickHouseClient> {
self.clickhouse.as_ref()
}
/// Get persistence configuration
pub const fn config(&self) -> &PersistenceConfig {
&self.config
}
/// Check health of all database connections
pub async fn health_check(&self) -> Result<HealthStatus, PersistenceError> {
self.health
.check_all_systems(
&self.postgres,
&self.influx,
&self.redis,
self.clickhouse.as_ref(),
)
.await
.map_err(|e| PersistenceError::Configuration(format!("Health check failed: {}", e)))
}
/// Run database migrations on `PostgreSQL`
pub async fn run_migrations(&self) -> Result<(), PersistenceError> {
run_pending_migrations(self.postgres.pool())
.await
.map(|_| ())
.map_err(|e| PersistenceError::Configuration(format!("Migration failed: {}", e)))
}
/// Perform backup operations
pub async fn backup(&self) -> Result<(), PersistenceError> {
create_full_backup(&self.config)
.await
.map(|_| ())
.map_err(|e| PersistenceError::Configuration(format!("Backup failed: {}", e)))
}
/// Get performance metrics from all systems
pub async fn get_performance_metrics(&self) -> Result<PersistenceMetrics, PersistenceError> {
Ok(PersistenceMetrics {
postgres: self.postgres.get_metrics().await?,
influx: self.influx.get_metrics().await?,
redis: self.redis.get_metrics().await?,
clickhouse: if let Some(ch) = &self.clickhouse {
Some(ch.get_metrics().await?)
} else {
// None variant
None
},
})
}
}
/// Performance metrics for all persistence systems
#[derive(Debug, Clone, Serialize, Deserialize)]
/// PersistenceMetrics
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct PersistenceMetrics {
/// Postgres
pub postgres: postgres::PostgresMetrics,
/// Influx
pub influx: influxdb::InfluxMetrics,
/// `Redis`
pub redis: redis::RedisMetrics,
/// Clickhouse
pub clickhouse: Option<clickhouse::ClickHouseMetrics>,
}
/// `Result` type for persistence operations
pub type PersistenceResult<T> = Result<T, PersistenceError>;