mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
Add multi-database data layer
Introduce aether-data-schema and driver-specific schema generation for Postgres, MySQL, and SQLite. Split data backends, lifecycle, repositories, and gateway runtime integration across database drivers. Verified with cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace.
This commit is contained in:
@@ -2,11 +2,11 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use aether_data::postgres::{
|
||||
use aether_data::driver::postgres::{
|
||||
DatabaseRecordId, PostgresLeaseClaimOptions, PostgresLeaseClaimSpec, PostgresLeaseRunnerConfig,
|
||||
PostgresPoolConfig,
|
||||
};
|
||||
use aether_data::redis::{
|
||||
use aether_data::driver::redis::{
|
||||
RedisClientConfig, RedisConsumerGroup, RedisConsumerName, RedisLockLease, RedisLockRunner,
|
||||
RedisLockRunnerConfig, RedisStreamName, RedisStreamReclaimConfig, RedisStreamRunner,
|
||||
RedisStreamRunnerConfig,
|
||||
@@ -532,7 +532,7 @@ async fn benchmark_redis_stream_ack_into(
|
||||
}
|
||||
|
||||
async fn benchmark_postgres_lease(
|
||||
runner: &aether_data::postgres::PostgresLeaseRunner,
|
||||
runner: &aether_data::driver::postgres::PostgresLeaseRunner,
|
||||
config: &DependencyPressureBaselineConfig,
|
||||
) -> Result<PostgresLeasePressureReport, Box<dyn std::error::Error>> {
|
||||
let spec = PostgresLeaseClaimSpec {
|
||||
@@ -608,7 +608,7 @@ async fn benchmark_postgres_lease(
|
||||
}
|
||||
|
||||
async fn record_postgres_lease_follow_up(
|
||||
runner: &aether_data::postgres::PostgresLeaseRunner,
|
||||
runner: &aether_data::driver::postgres::PostgresLeaseRunner,
|
||||
spec: &PostgresLeaseClaimSpec,
|
||||
owner: &str,
|
||||
ids: &[DatabaseRecordId],
|
||||
|
||||
@@ -3,11 +3,11 @@ use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use aether_data::postgres::{
|
||||
use aether_data::driver::postgres::{
|
||||
PostgresLeaseClaimOptions, PostgresLeaseClaimSpec, PostgresLeaseRunnerConfig,
|
||||
PostgresPoolConfig, PostgresTransactionOptions,
|
||||
};
|
||||
use aether_data::redis::{RedisClientConfig, RedisLockRunnerConfig};
|
||||
use aether_data::driver::redis::{RedisClientConfig, RedisLockRunnerConfig};
|
||||
use aether_data::{DataLayerError, PostgresBackend, RedisBackend};
|
||||
use aether_testkit::{
|
||||
init_test_runtime_for, reserve_local_port, ManagedPostgresServer, ManagedRedisServer,
|
||||
|
||||
@@ -4,14 +4,13 @@ use std::time::Duration;
|
||||
use aether_gateway::tunnel_protocol as protocol;
|
||||
use aether_gateway::GatewayDataConfig;
|
||||
use aether_testkit::{
|
||||
init_test_runtime_for, reserve_local_port, run_http_load_probe, wait_until, GatewayHarness,
|
||||
GatewayHarnessConfig, HttpLoadProbeConfig, HttpLoadProbeResponseMode, HttpLoadProbeResult,
|
||||
ManagedPostgresServer, ManagedRedisServer,
|
||||
init_test_runtime_for, prepare_aether_postgres_schema, reserve_local_port, run_http_load_probe,
|
||||
wait_until, GatewayHarness, GatewayHarnessConfig, HttpLoadProbeConfig,
|
||||
HttpLoadProbeResponseMode, HttpLoadProbeResult, ManagedPostgresServer, ManagedRedisServer,
|
||||
};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use reqwest::Method;
|
||||
use serde::Serialize;
|
||||
use sqlx::{Connection, Executor, PgConnection};
|
||||
use tokio_tungstenite::tungstenite::client::IntoClientRequest;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
|
||||
@@ -113,7 +112,7 @@ async fn run_suite(
|
||||
})
|
||||
.expect("postgres url should be resolved");
|
||||
|
||||
ensure_owner_relay_schema(&postgres_url).await?;
|
||||
prepare_aether_postgres_schema(&postgres_url).await?;
|
||||
|
||||
let key_prefix = format!("aether-owner-relay-baseline-{}", std::process::id());
|
||||
let shared_data = GatewayDataConfig::from_postgres_url(postgres_url.clone(), false)
|
||||
@@ -206,89 +205,6 @@ async fn run_suite(
|
||||
})
|
||||
}
|
||||
|
||||
async fn ensure_owner_relay_schema(postgres_url: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut connection = PgConnection::connect(postgres_url).await?;
|
||||
connection
|
||||
.execute(
|
||||
r#"
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'proxynodestatus') THEN
|
||||
CREATE TYPE proxynodestatus AS ENUM ('online', 'offline');
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
"#,
|
||||
)
|
||||
.await?;
|
||||
connection
|
||||
.execute(
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS system_configs (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
key VARCHAR(100) UNIQUE NOT NULL,
|
||||
value JSON NOT NULL,
|
||||
description TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.await?;
|
||||
connection
|
||||
.execute(
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS proxy_nodes (
|
||||
id VARCHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
ip VARCHAR(512) NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
region VARCHAR(100) NULL,
|
||||
is_manual BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
proxy_url VARCHAR(500) NULL,
|
||||
proxy_username VARCHAR(255) NULL,
|
||||
proxy_password VARCHAR(500) NULL,
|
||||
status proxynodestatus NOT NULL DEFAULT 'online',
|
||||
registered_by VARCHAR(36) NULL,
|
||||
last_heartbeat_at TIMESTAMPTZ NULL,
|
||||
heartbeat_interval INTEGER NOT NULL DEFAULT 30,
|
||||
active_connections INTEGER NOT NULL DEFAULT 0,
|
||||
total_requests BIGINT NOT NULL DEFAULT 0,
|
||||
avg_latency_ms DOUBLE PRECISION NULL,
|
||||
failed_requests BIGINT NOT NULL DEFAULT 0,
|
||||
dns_failures BIGINT NOT NULL DEFAULT 0,
|
||||
stream_errors BIGINT NOT NULL DEFAULT 0,
|
||||
proxy_metadata JSON NULL,
|
||||
hardware_info JSON NULL,
|
||||
estimated_max_concurrency INTEGER NULL,
|
||||
tunnel_mode BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
tunnel_connected BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
tunnel_connected_at TIMESTAMPTZ NULL,
|
||||
remote_config JSON NULL,
|
||||
config_version INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.await?;
|
||||
connection
|
||||
.execute(
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS proxy_node_events (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
node_id VARCHAR(36) NOT NULL,
|
||||
event_type TEXT NOT NULL,
|
||||
detail TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.await?;
|
||||
connection.close().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_owner_attachment(forwarder_base_url: &str) -> Result<(), String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(2))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use aether_data::redis::{
|
||||
use aether_data::driver::redis::{
|
||||
RedisClientConfig, RedisConsumerGroup, RedisConsumerName, RedisStreamReclaimConfig,
|
||||
RedisStreamRunnerConfig,
|
||||
};
|
||||
@@ -124,8 +124,8 @@ async fn run_suite(
|
||||
}
|
||||
|
||||
async fn benchmark_append(
|
||||
runner: &aether_data::redis::RedisStreamRunner,
|
||||
stream: &aether_data::redis::RedisStreamName,
|
||||
runner: &aether_data::driver::redis::RedisStreamRunner,
|
||||
stream: &aether_data::driver::redis::RedisStreamName,
|
||||
config: &RedisWorkerBaselineConfig,
|
||||
) -> Result<OperationSummary, Box<dyn std::error::Error>> {
|
||||
let next = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||
@@ -182,8 +182,8 @@ async fn benchmark_append(
|
||||
}
|
||||
|
||||
async fn benchmark_read_group(
|
||||
runner: &aether_data::redis::RedisStreamRunner,
|
||||
stream: &aether_data::redis::RedisStreamName,
|
||||
runner: &aether_data::driver::redis::RedisStreamRunner,
|
||||
stream: &aether_data::driver::redis::RedisStreamName,
|
||||
group: &RedisConsumerGroup,
|
||||
consumer: &RedisConsumerName,
|
||||
config: &RedisWorkerBaselineConfig,
|
||||
@@ -211,8 +211,8 @@ async fn benchmark_read_group(
|
||||
}
|
||||
|
||||
async fn benchmark_reclaim(
|
||||
runner: &aether_data::redis::RedisStreamRunner,
|
||||
stream: &aether_data::redis::RedisStreamName,
|
||||
runner: &aether_data::driver::redis::RedisStreamRunner,
|
||||
stream: &aether_data::driver::redis::RedisStreamName,
|
||||
group: &RedisConsumerGroup,
|
||||
consumer_a: &RedisConsumerName,
|
||||
consumer_b: &RedisConsumerName,
|
||||
@@ -283,8 +283,8 @@ async fn benchmark_reclaim(
|
||||
}
|
||||
|
||||
async fn benchmark_ack(
|
||||
runner: &aether_data::redis::RedisStreamRunner,
|
||||
stream: &aether_data::redis::RedisStreamName,
|
||||
runner: &aether_data::driver::redis::RedisStreamRunner,
|
||||
stream: &aether_data::driver::redis::RedisStreamName,
|
||||
group: &RedisConsumerGroup,
|
||||
ids: &[String],
|
||||
) -> Result<OperationSummary, Box<dyn std::error::Error>> {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub use load::{
|
||||
pub use metrics::{
|
||||
fetch_prometheus_samples, find_metric_value_u64, parse_prometheus_samples, PrometheusSample,
|
||||
};
|
||||
pub use postgres::ManagedPostgresServer;
|
||||
pub use postgres::{prepare_aether_postgres_schema, ManagedPostgresServer};
|
||||
pub use redis::ManagedRedisServer;
|
||||
pub use server::{reserve_local_port, SpawnedServer};
|
||||
pub use tracing::{init_test_runtime, init_test_runtime_for, test_runtime_config};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
|
||||
use aether_data::driver::postgres::PostgresPoolConfig;
|
||||
use aether_data::{DataBackends, DataLayerConfig};
|
||||
use sqlx::{Connection, PgConnection};
|
||||
|
||||
use crate::wait_until;
|
||||
@@ -140,6 +142,26 @@ impl Drop for ManagedPostgresServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn prepare_aether_postgres_schema(
|
||||
database_url: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = PostgresPoolConfig {
|
||||
database_url: database_url.to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let backends = DataBackends::from_config(DataLayerConfig::from_postgres(config))?;
|
||||
let pending_migrations = backends
|
||||
.prepare_database_for_startup()
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
if !pending_migrations.is_empty() {
|
||||
backends.run_database_migrations().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reserve_local_port() -> Result<u16, std::io::Error> {
|
||||
let listener = std::net::TcpListener::bind("127.0.0.1:0")?;
|
||||
let port = listener.local_addr()?.port();
|
||||
|
||||
Reference in New Issue
Block a user