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>
268 lines
9.7 KiB
Rust
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>;
|