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:
@@ -9,6 +9,7 @@ description = "Shared data contracts and repository traits for Aether Rust servi
|
||||
[dependencies]
|
||||
aether-ai-formats.workspace = true
|
||||
async-trait.workspace = true
|
||||
chrono.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -12,6 +12,9 @@ pub enum DataLayerError {
|
||||
#[error("redis error: {0}")]
|
||||
Redis(String),
|
||||
|
||||
#[error("sql error: {0}")]
|
||||
Sql(String),
|
||||
|
||||
#[error("operation timed out: {0}")]
|
||||
TimedOut(String),
|
||||
|
||||
@@ -27,4 +30,8 @@ impl DataLayerError {
|
||||
pub fn redis(error: impl std::fmt::Display) -> Self {
|
||||
Self::Redis(error.to_string())
|
||||
}
|
||||
|
||||
pub fn sql(error: impl std::fmt::Display) -> Self {
|
||||
Self::Sql(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod types;
|
||||
|
||||
pub use types::{
|
||||
AdminBillingCollectorRecord, AdminBillingCollectorWriteInput, AdminBillingPresetApplyResult,
|
||||
AdminBillingRuleRecord, AdminBillingRuleWriteInput, BillingReadRepository,
|
||||
StoredBillingModelContext,
|
||||
AdminBillingCollectorRecord, AdminBillingCollectorWriteInput, AdminBillingMutationOutcome,
|
||||
AdminBillingPresetApplyResult, AdminBillingRuleRecord, AdminBillingRuleWriteInput,
|
||||
BillingReadRepository, StoredBillingModelContext,
|
||||
};
|
||||
|
||||
@@ -142,6 +142,14 @@ pub struct AdminBillingPresetApplyResult {
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum AdminBillingMutationOutcome<T> {
|
||||
Applied(T),
|
||||
NotFound,
|
||||
Invalid(String),
|
||||
Unavailable,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait BillingReadRepository: Send + Sync {
|
||||
async fn find_model_context(
|
||||
@@ -160,4 +168,109 @@ pub trait BillingReadRepository: Send + Sync {
|
||||
let _ = (provider_id, provider_api_key_id, model_id);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn admin_billing_enabled_default_value_exists(
|
||||
&self,
|
||||
api_format: &str,
|
||||
task_type: &str,
|
||||
dimension_name: &str,
|
||||
existing_id: Option<&str>,
|
||||
) -> Result<Option<bool>, crate::DataLayerError> {
|
||||
let _ = (api_format, task_type, dimension_name, existing_id);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn create_admin_billing_rule(
|
||||
&self,
|
||||
input: &AdminBillingRuleWriteInput,
|
||||
) -> Result<AdminBillingMutationOutcome<AdminBillingRuleRecord>, crate::DataLayerError> {
|
||||
let _ = input;
|
||||
Ok(AdminBillingMutationOutcome::Unavailable)
|
||||
}
|
||||
|
||||
async fn list_admin_billing_rules(
|
||||
&self,
|
||||
task_type: Option<&str>,
|
||||
is_enabled: Option<bool>,
|
||||
page: u32,
|
||||
page_size: u32,
|
||||
) -> Result<Option<(Vec<AdminBillingRuleRecord>, u64)>, crate::DataLayerError> {
|
||||
let _ = (task_type, is_enabled, page, page_size);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn find_admin_billing_rule(
|
||||
&self,
|
||||
rule_id: &str,
|
||||
) -> Result<Option<AdminBillingRuleRecord>, crate::DataLayerError> {
|
||||
let _ = rule_id;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn update_admin_billing_rule(
|
||||
&self,
|
||||
rule_id: &str,
|
||||
input: &AdminBillingRuleWriteInput,
|
||||
) -> Result<AdminBillingMutationOutcome<AdminBillingRuleRecord>, crate::DataLayerError> {
|
||||
let _ = (rule_id, input);
|
||||
Ok(AdminBillingMutationOutcome::Unavailable)
|
||||
}
|
||||
|
||||
async fn create_admin_billing_collector(
|
||||
&self,
|
||||
input: &AdminBillingCollectorWriteInput,
|
||||
) -> Result<AdminBillingMutationOutcome<AdminBillingCollectorRecord>, crate::DataLayerError>
|
||||
{
|
||||
let _ = input;
|
||||
Ok(AdminBillingMutationOutcome::Unavailable)
|
||||
}
|
||||
|
||||
async fn list_admin_billing_collectors(
|
||||
&self,
|
||||
api_format: Option<&str>,
|
||||
task_type: Option<&str>,
|
||||
dimension_name: Option<&str>,
|
||||
is_enabled: Option<bool>,
|
||||
page: u32,
|
||||
page_size: u32,
|
||||
) -> Result<Option<(Vec<AdminBillingCollectorRecord>, u64)>, crate::DataLayerError> {
|
||||
let _ = (
|
||||
api_format,
|
||||
task_type,
|
||||
dimension_name,
|
||||
is_enabled,
|
||||
page,
|
||||
page_size,
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn find_admin_billing_collector(
|
||||
&self,
|
||||
collector_id: &str,
|
||||
) -> Result<Option<AdminBillingCollectorRecord>, crate::DataLayerError> {
|
||||
let _ = collector_id;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn update_admin_billing_collector(
|
||||
&self,
|
||||
collector_id: &str,
|
||||
input: &AdminBillingCollectorWriteInput,
|
||||
) -> Result<AdminBillingMutationOutcome<AdminBillingCollectorRecord>, crate::DataLayerError>
|
||||
{
|
||||
let _ = (collector_id, input);
|
||||
Ok(AdminBillingMutationOutcome::Unavailable)
|
||||
}
|
||||
|
||||
async fn apply_admin_billing_preset(
|
||||
&self,
|
||||
preset: &str,
|
||||
mode: &str,
|
||||
collectors: &[AdminBillingCollectorWriteInput],
|
||||
) -> Result<AdminBillingMutationOutcome<AdminBillingPresetApplyResult>, crate::DataLayerError>
|
||||
{
|
||||
let _ = (preset, mode, collectors);
|
||||
Ok(AdminBillingMutationOutcome::Unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
mod types;
|
||||
|
||||
pub use types::{
|
||||
parse_usage_body_ref, usage_body_ref, StoredProviderApiKeyUsageSummary,
|
||||
StoredProviderUsageSummary, StoredProviderUsageWindow, StoredRequestUsageAudit,
|
||||
StoredUsageAuditAggregation, StoredUsageAuditSummary, StoredUsageBreakdownSummaryRow,
|
||||
StoredUsageCacheAffinityHitSummary, StoredUsageCacheAffinityIntervalRow,
|
||||
StoredUsageCacheHitSummary, StoredUsageCostSavingsSummary, StoredUsageDailySummary,
|
||||
StoredUsageDashboardDailyBreakdownRow, StoredUsageDashboardProviderCount,
|
||||
StoredUsageDashboardSummary, StoredUsageErrorDistributionRow, StoredUsageLeaderboardSummary,
|
||||
parse_usage_body_ref, usage_body_ref, PendingUsageCleanupSummary,
|
||||
StoredProviderApiKeyUsageSummary, StoredProviderUsageSummary, StoredProviderUsageWindow,
|
||||
StoredRequestUsageAudit, StoredUsageAuditAggregation, StoredUsageAuditSummary,
|
||||
StoredUsageBreakdownSummaryRow, StoredUsageCacheAffinityHitSummary,
|
||||
StoredUsageCacheAffinityIntervalRow, StoredUsageCacheHitSummary, StoredUsageCostSavingsSummary,
|
||||
StoredUsageDailySummary, StoredUsageDashboardDailyBreakdownRow,
|
||||
StoredUsageDashboardProviderCount, StoredUsageDashboardSummary,
|
||||
StoredUsageErrorDistributionRow, StoredUsageLeaderboardSummary,
|
||||
StoredUsagePerformancePercentilesRow, StoredUsageSettledCostSummary,
|
||||
StoredUsageTimeSeriesBucket, StoredUsageUserTotals, UpsertUsageRecord,
|
||||
UsageAuditAggregationGroupBy, UsageAuditAggregationQuery, UsageAuditKeywordSearchQuery,
|
||||
UsageAuditListQuery, UsageAuditSummaryQuery, UsageBodyCaptureResult, UsageBodyCaptureState,
|
||||
UsageBodyCaptureStorage, UsageBodyField, UsageBreakdownGroupBy, UsageBreakdownSummaryQuery,
|
||||
UsageCacheAffinityHitSummaryQuery, UsageCacheAffinityIntervalGroupBy,
|
||||
UsageCacheAffinityIntervalQuery, UsageCacheHitSummaryQuery, UsageCostSavingsSummaryQuery,
|
||||
UsageDailyHeatmapQuery, UsageDashboardDailyBreakdownQuery, UsageDashboardProviderCountsQuery,
|
||||
UsageCacheAffinityIntervalQuery, UsageCacheHitSummaryQuery, UsageCleanupSummary,
|
||||
UsageCleanupWindow, UsageCostSavingsSummaryQuery, UsageDailyHeatmapQuery,
|
||||
UsageDashboardDailyBreakdownQuery, UsageDashboardProviderCountsQuery,
|
||||
UsageDashboardSummaryQuery, UsageErrorDistributionQuery, UsageLeaderboardGroupBy,
|
||||
UsageLeaderboardQuery, UsageMonitoringErrorCountQuery, UsageMonitoringErrorListQuery,
|
||||
UsagePerformancePercentilesQuery, UsageReadRepository, UsageRepository,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_json::Value;
|
||||
|
||||
/// Joined usage read model assembled from the accounting row plus the newer audit/snapshot
|
||||
@@ -1568,12 +1569,57 @@ pub trait UsageWriteRepository: Send + Sync {
|
||||
async fn rebuild_api_key_usage_stats(&self) -> Result<u64, crate::DataLayerError>;
|
||||
|
||||
async fn rebuild_provider_api_key_usage_stats(&self) -> Result<u64, crate::DataLayerError>;
|
||||
|
||||
async fn cleanup_stale_pending_requests(
|
||||
&self,
|
||||
cutoff_unix_secs: u64,
|
||||
now_unix_secs: u64,
|
||||
timeout_minutes: u64,
|
||||
batch_size: usize,
|
||||
) -> Result<PendingUsageCleanupSummary, crate::DataLayerError> {
|
||||
let _ = (cutoff_unix_secs, now_unix_secs, timeout_minutes, batch_size);
|
||||
Ok(PendingUsageCleanupSummary::default())
|
||||
}
|
||||
|
||||
async fn cleanup_usage(
|
||||
&self,
|
||||
window: &UsageCleanupWindow,
|
||||
batch_size: usize,
|
||||
auto_delete_expired_keys: bool,
|
||||
) -> Result<UsageCleanupSummary, crate::DataLayerError> {
|
||||
let _ = (window, batch_size, auto_delete_expired_keys);
|
||||
Ok(UsageCleanupSummary::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UsageRepository: UsageReadRepository + UsageWriteRepository + Send + Sync {}
|
||||
|
||||
impl<T> UsageRepository for T where T: UsageReadRepository + UsageWriteRepository + Send + Sync {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct PendingUsageCleanupSummary {
|
||||
pub failed: usize,
|
||||
pub recovered: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UsageCleanupSummary {
|
||||
pub body_externalized: usize,
|
||||
pub legacy_body_refs_migrated: usize,
|
||||
pub body_cleaned: usize,
|
||||
pub header_cleaned: usize,
|
||||
pub keys_cleaned: usize,
|
||||
pub records_deleted: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UsageCleanupWindow {
|
||||
pub detail_cutoff: DateTime<Utc>,
|
||||
pub compressed_cutoff: DateTime<Utc>,
|
||||
pub header_cutoff: DateTime<Utc>,
|
||||
pub log_cutoff: DateTime<Utc>,
|
||||
}
|
||||
|
||||
fn parse_u64(value: i32, field_name: &str) -> Result<u64, crate::DataLayerError> {
|
||||
u64::try_from(value).map_err(|_| {
|
||||
crate::DataLayerError::UnexpectedValue(format!("invalid {field_name}: {value}"))
|
||||
|
||||
Reference in New Issue
Block a user