perf: reduce gateway db pressure under load

This commit is contained in:
elky
2026-06-22 00:08:48 +08:00
parent 541cc197c4
commit f75894acbb
52 changed files with 4440 additions and 252 deletions
@@ -0,0 +1,180 @@
use std::sync::Arc;
use std::time::Duration;
use aether_cache::ExpiringMap;
use aether_data::repository::auth::*;
use aether_data::DataLayerError;
use async_trait::async_trait;
const AUTH_API_KEY_SNAPSHOT_CACHE_TTL: Duration = Duration::from_secs(30);
const AUTH_API_KEY_SNAPSHOT_CACHE_MAX_ENTRIES: usize = 16_384;
pub(super) struct CachedAuthApiKeyReadRepository {
inner: Arc<dyn AuthApiKeyReadRepository>,
snapshots: ExpiringMap<AuthApiKeySnapshotCacheKey, Option<StoredAuthApiKeySnapshot>>,
load_guard: tokio::sync::Mutex<()>,
}
impl CachedAuthApiKeyReadRepository {
pub(super) fn new(inner: Arc<dyn AuthApiKeyReadRepository>) -> Self {
Self {
inner,
snapshots: ExpiringMap::new(),
load_guard: tokio::sync::Mutex::new(()),
}
}
fn cache_key(key: AuthApiKeyLookupKey<'_>) -> AuthApiKeySnapshotCacheKey {
match key {
AuthApiKeyLookupKey::KeyHash(value) => {
AuthApiKeySnapshotCacheKey::KeyHash(value.to_string())
}
AuthApiKeyLookupKey::ApiKeyId(value) => {
AuthApiKeySnapshotCacheKey::ApiKeyId(value.to_string())
}
AuthApiKeyLookupKey::UserApiKeyIds {
user_id,
api_key_id,
} => AuthApiKeySnapshotCacheKey::UserApiKeyIds {
user_id: user_id.to_string(),
api_key_id: api_key_id.to_string(),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum AuthApiKeySnapshotCacheKey {
KeyHash(String),
ApiKeyId(String),
UserApiKeyIds { user_id: String, api_key_id: String },
}
#[async_trait]
impl AuthApiKeyReadRepository for CachedAuthApiKeyReadRepository {
async fn find_api_key_snapshot(
&self,
key: AuthApiKeyLookupKey<'_>,
) -> Result<Option<StoredAuthApiKeySnapshot>, DataLayerError> {
let cache_key = Self::cache_key(key);
if let Some(value) = self
.snapshots
.get_fresh(&cache_key, AUTH_API_KEY_SNAPSHOT_CACHE_TTL)
{
return Ok(value);
}
let _guard = self.load_guard.lock().await;
if let Some(value) = self
.snapshots
.get_fresh(&cache_key, AUTH_API_KEY_SNAPSHOT_CACHE_TTL)
{
return Ok(value);
}
let value = self.inner.find_api_key_snapshot(key).await?;
self.snapshots.insert(
cache_key,
value.clone(),
AUTH_API_KEY_SNAPSHOT_CACHE_TTL,
AUTH_API_KEY_SNAPSHOT_CACHE_MAX_ENTRIES,
);
Ok(value)
}
async fn list_api_key_snapshots_by_ids(
&self,
api_key_ids: &[String],
) -> Result<Vec<StoredAuthApiKeySnapshot>, DataLayerError> {
let mut snapshots = Vec::with_capacity(api_key_ids.len());
for api_key_id in api_key_ids {
if let Some(snapshot) = self
.find_api_key_snapshot(AuthApiKeyLookupKey::ApiKeyId(api_key_id))
.await?
{
snapshots.push(snapshot);
}
}
Ok(snapshots)
}
async fn list_export_api_keys_by_user_ids(
&self,
user_ids: &[String],
) -> Result<Vec<StoredAuthApiKeyExportRecord>, DataLayerError> {
self.inner.list_export_api_keys_by_user_ids(user_ids).await
}
async fn list_export_api_keys_by_ids(
&self,
api_key_ids: &[String],
) -> Result<Vec<StoredAuthApiKeyExportRecord>, DataLayerError> {
self.inner.list_export_api_keys_by_ids(api_key_ids).await
}
async fn list_export_api_keys_by_name_search(
&self,
name_search: &str,
) -> Result<Vec<StoredAuthApiKeyExportRecord>, DataLayerError> {
self.inner
.list_export_api_keys_by_name_search(name_search)
.await
}
async fn list_export_standalone_api_keys_page(
&self,
query: &StandaloneApiKeyExportListQuery,
) -> Result<Vec<StoredAuthApiKeyExportRecord>, DataLayerError> {
self.inner.list_export_standalone_api_keys_page(query).await
}
async fn count_export_standalone_api_keys(
&self,
is_active: Option<bool>,
) -> Result<u64, DataLayerError> {
self.inner.count_export_standalone_api_keys(is_active).await
}
async fn summarize_export_api_keys_by_user_ids(
&self,
user_ids: &[String],
now_unix_secs: u64,
) -> Result<AuthApiKeyExportSummary, DataLayerError> {
self.inner
.summarize_export_api_keys_by_user_ids(user_ids, now_unix_secs)
.await
}
async fn summarize_export_non_standalone_api_keys(
&self,
now_unix_secs: u64,
) -> Result<AuthApiKeyExportSummary, DataLayerError> {
self.inner
.summarize_export_non_standalone_api_keys(now_unix_secs)
.await
}
async fn summarize_export_standalone_api_keys(
&self,
now_unix_secs: u64,
) -> Result<AuthApiKeyExportSummary, DataLayerError> {
self.inner
.summarize_export_standalone_api_keys(now_unix_secs)
.await
}
async fn find_export_standalone_api_key_by_id(
&self,
api_key_id: &str,
) -> Result<Option<StoredAuthApiKeyExportRecord>, DataLayerError> {
self.inner
.find_export_standalone_api_key_by_id(api_key_id)
.await
}
async fn list_export_standalone_api_keys(
&self,
) -> Result<Vec<StoredAuthApiKeyExportRecord>, DataLayerError> {
self.inner.list_export_standalone_api_keys().await
}
}
@@ -406,6 +406,7 @@ mod tests {
struct StubCandidateSelectionRepository {
calls: AtomicUsize,
delay: Duration,
rows: Vec<StoredMinimalCandidateSelectionRow>,
}
impl StubCandidateSelectionRepository {
@@ -413,6 +414,7 @@ mod tests {
Self {
calls: AtomicUsize::new(0),
delay,
rows: Vec::new(),
}
}
@@ -425,7 +427,7 @@ mod tests {
if !self.delay.is_zero() {
tokio::time::sleep(self.delay).await;
}
Ok(Vec::new())
Ok(self.rows.clone())
}
}
@@ -697,4 +699,84 @@ mod tests {
leader.abort();
let _ = leader.await;
}
#[tokio::test]
async fn candidate_selection_load_balance_cache_keeps_seed_specific_entries() {
let inner = Arc::new(StubCandidateSelectionRepository {
calls: AtomicUsize::new(0),
delay: Duration::ZERO,
rows: vec![
sample_row("key-a", 1),
sample_row("key-b", 2),
sample_row("key-c", 3),
],
});
let cache = CachedMinimalCandidateSelectionReadRepository::new(inner.clone());
let query = |seed: &str| StoredPoolKeyCandidateRowsQuery {
api_format: "openai:chat".to_string(),
provider_id: "provider-1".to_string(),
endpoint_id: "endpoint-1".to_string(),
model_id: "model-1".to_string(),
selected_provider_model_name: "mock-model".to_string(),
order: StoredPoolKeyCandidateOrder::LoadBalance {
seed: seed.to_string(),
},
offset: 0,
limit: 2,
};
let first = cache
.list_pool_key_rows_for_group(&query("seed-a"))
.await
.unwrap();
let second = cache
.list_pool_key_rows_for_group(&query("seed-b"))
.await
.unwrap();
assert_eq!(inner.calls(), 2);
assert_eq!(first.len(), 3);
assert_eq!(second.len(), 3);
let third = cache
.list_pool_key_rows_for_group(&query("seed-a"))
.await
.unwrap();
assert_eq!(inner.calls(), 2);
assert_eq!(third.len(), 3);
}
fn sample_row(key_id: &str, key_internal_priority: i32) -> StoredMinimalCandidateSelectionRow {
StoredMinimalCandidateSelectionRow {
provider_id: "provider-1".to_string(),
provider_name: "provider".to_string(),
provider_type: "custom".to_string(),
provider_priority: 1,
provider_is_active: true,
endpoint_id: "endpoint-1".to_string(),
endpoint_api_format: "openai:chat".to_string(),
endpoint_api_family: Some("openai".to_string()),
endpoint_kind: Some("chat".to_string()),
endpoint_is_active: true,
key_id: key_id.to_string(),
key_name: key_id.to_string(),
key_auth_type: "api_key".to_string(),
key_is_active: true,
key_api_formats: Some(vec!["openai:chat".to_string()]),
key_allowed_models: None,
key_capabilities: None,
key_internal_priority,
key_global_priority_by_format: None,
model_id: "model-1".to_string(),
global_model_id: "global-model-1".to_string(),
global_model_name: "mock-model".to_string(),
global_model_mappings: None,
global_model_supports_streaming: Some(true),
model_provider_model_name: "mock-model".to_string(),
model_provider_model_mappings: None,
model_supports_streaming: Some(true),
model_is_active: true,
model_is_available: true,
}
}
}
+77 -8
View File
@@ -1,11 +1,21 @@
use aether_data::{DataBackends, DataLayerError, DatabaseDriver};
use aether_data_contracts::repository::candidate_selection::MinimalCandidateSelectionReadRepository;
use aether_data_contracts::repository::candidates::RequestCandidateReadRepository;
use aether_data_contracts::repository::provider_catalog::ProviderCatalogReadRepository;
use aether_runtime_state::RuntimeQueueStore;
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::time::{Duration, Instant};
use tokio::sync::Mutex;
use super::{GatewayDataConfig, GatewayDataState, StoredSystemConfigEntry};
const SYSTEM_CONFIG_VALUE_CACHE_TTL: Duration = Duration::from_secs(30);
fn system_config_value_load_guard() -> &'static Mutex<()> {
static GUARD: OnceLock<Mutex<()>> = OnceLock::new();
GUARD.get_or_init(|| Mutex::new(()))
}
fn current_system_config_updated_at_unix_secs() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
@@ -64,11 +74,16 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
});
}
let backends = DataBackends::from_config(config.to_data_layer_config())?;
let auth_api_key_reader = backends.read().auth_api_keys();
let auth_api_key_reader = backends.read().auth_api_keys().map(|repository| {
Arc::new(super::auth_api_key_cache::CachedAuthApiKeyReadRepository::new(repository))
as Arc<dyn aether_data::repository::auth::AuthApiKeyReadRepository>
});
let auth_api_key_writer = backends.write().auth_api_keys();
let auth_module_reader = backends.read().auth_modules();
let auth_module_writer = backends.write().auth_modules();
@@ -97,7 +112,13 @@ impl GatewayDataState {
),
) as Arc<dyn MinimalCandidateSelectionReadRepository>
});
let request_candidate_reader = backends.read().request_candidates();
let request_candidate_reader = backends.read().request_candidates().map(|repository| {
Arc::new(
super::request_candidate_cache::CachedRequestCandidateReadRepository::new(
repository,
),
) as Arc<dyn RequestCandidateReadRepository>
});
let request_candidate_writer = backends.write().request_candidates();
let gemini_file_mapping_writer = backends.write().gemini_file_mappings();
let provider_catalog_reader = backends.read().provider_catalog().map(|repository| {
@@ -110,7 +131,11 @@ impl GatewayDataState {
let pool_score_writer = backends.write().pool_scores();
let provider_quota_reader = backends.read().provider_quotas();
let provider_quota_writer = backends.write().provider_quotas();
let routing_group_reader = backends.read().routing_groups();
let routing_group_reader = backends.read().routing_groups().map(|repository| {
Arc::new(super::routing_group_cache::CachedRoutingGroupReadRepository::new(
repository,
)) as Arc<dyn aether_data_contracts::repository::routing_profiles::RoutingGroupReadRepository>
});
let routing_group_writer = backends.write().routing_groups();
let usage_reader = backends.read().usage();
let usage_writer = backends.write().usage();
@@ -166,6 +191,8 @@ impl GatewayDataState {
wallet_writer,
settlement_writer,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
})
}
@@ -401,10 +428,38 @@ impl GatewayDataState {
.get(key)
.map(|entry| entry.value.clone()));
}
let cached_value = self
.system_config_value_cache
.read()
.expect("system config value cache lock")
.get(key)
.cloned();
if let Some((cached_at, value)) = cached_value {
if cached_at.elapsed() <= SYSTEM_CONFIG_VALUE_CACHE_TTL {
return Ok(value);
}
}
let _guard = system_config_value_load_guard().lock().await;
let cached_value = self
.system_config_value_cache
.read()
.expect("system config value cache lock")
.get(key)
.cloned();
if let Some((cached_at, value)) = cached_value {
if cached_at.elapsed() <= SYSTEM_CONFIG_VALUE_CACHE_TTL {
return Ok(value);
}
}
let Some(backends) = self.backends.as_ref() else {
return Ok(None);
};
backends.find_system_config_value(key).await
let value = backends.find_system_config_value(key).await?;
self.system_config_value_cache
.write()
.expect("system config value cache lock")
.insert(key.to_string(), (Instant::now(), value.clone()));
Ok(value)
}
pub(crate) async fn upsert_system_config_value(
@@ -454,6 +509,7 @@ impl GatewayDataState {
updated_at_unix_secs: Some(current_system_config_updated_at_unix_secs()),
};
values.insert(key.to_string(), entry.clone());
self.clear_cached_system_config_value(key);
return Ok(entry);
}
if let Some(backends) = self.backends.as_ref() {
@@ -461,9 +517,11 @@ impl GatewayDataState {
.upsert_system_config_entry(key, value, description)
.await?
{
self.clear_cached_system_config_value(key);
return Ok(entry);
}
}
self.clear_cached_system_config_value(key);
Ok(StoredSystemConfigEntry {
key: key.to_string(),
value: value.clone(),
@@ -477,16 +535,27 @@ impl GatewayDataState {
key: &str,
) -> Result<bool, DataLayerError> {
if let Some(values) = &self.system_config_values {
return Ok(values
let deleted = values
.write()
.expect("system config values lock")
.remove(key)
.is_some());
.is_some();
self.clear_cached_system_config_value(key);
return Ok(deleted);
}
let Some(backends) = self.backends.as_ref() else {
return Ok(false);
};
backends.delete_system_config_value(key).await
let deleted = backends.delete_system_config_value(key).await?;
self.clear_cached_system_config_value(key);
Ok(deleted)
}
fn clear_cached_system_config_value(&self, key: &str) {
self.system_config_value_cache
.write()
.expect("system config value cache lock")
.remove(key);
}
pub(crate) async fn read_admin_system_stats(
@@ -253,12 +253,15 @@ impl UsageRuntimeAccess for GatewayDataState {
}
async fn body_capture_policy(&self) -> Result<UsageBodyCapturePolicy, DataLayerError> {
let value = GatewayDataState::find_system_config_value(self, REQUEST_RECORD_LEVEL_KEY)
let value = match GatewayDataState::find_system_config_value(self, REQUEST_RECORD_LEVEL_KEY)
.await?
.or(
{
Some(value) => Some(value),
None => {
GatewayDataState::find_system_config_value(self, LEGACY_REQUEST_LOG_LEVEL_KEY)
.await?,
);
.await?
}
};
let max_request_body_size =
GatewayDataState::find_system_config_value(self, MAX_REQUEST_BODY_SIZE_KEY).await?;
let max_response_body_size =
+23
View File
@@ -1,7 +1,9 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
use std::sync::RwLock;
use std::time::Instant;
use super::auth::GatewayAuthApiKeySnapshot;
use super::candidates::{read_request_candidate_trace, RequestCandidateTrace};
@@ -194,6 +196,24 @@ pub(crate) struct GatewayDataState {
wallet_writer: Option<Arc<dyn WalletWriteRepository>>,
settlement_writer: Option<Arc<dyn SettlementWriteRepository>>,
system_config_values: Option<Arc<RwLock<BTreeMap<String, StoredSystemConfigEntry>>>>,
system_config_value_cache: Arc<RwLock<BTreeMap<String, (Instant, Option<serde_json::Value>)>>>,
billing_model_context_cache: Arc<
RwLock<HashMap<BillingModelContextCacheKey, (Instant, Option<StoredBillingModelContext>)>>,
>,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(super) enum BillingModelContextCacheKey {
ByModelId {
provider_id: String,
provider_api_key_id: Option<String>,
model_id: String,
},
ByGlobalModelName {
provider_id: String,
provider_api_key_id: Option<String>,
global_model_name: String,
},
}
impl fmt::Debug for GatewayDataState {
@@ -318,6 +338,7 @@ impl fmt::Debug for GatewayDataState {
}
mod auth;
mod auth_api_key_cache;
mod candidate_cache;
mod catalog;
mod core;
@@ -326,6 +347,8 @@ mod models;
mod pool_scores;
mod provider_catalog_cache;
mod referrals;
mod request_candidate_cache;
mod routing_group_cache;
mod routing_profiles;
mod runtime;
#[cfg(test)]
@@ -174,7 +174,19 @@ impl ProviderCatalogReadRepository for CachedProviderCatalogReadRepository {
&self,
endpoint_ids: &[String],
) -> Result<Vec<StoredProviderCatalogEndpoint>, DataLayerError> {
self.inner.list_endpoints_by_ids(endpoint_ids).await
let key = ProviderCatalogCacheKey::EndpointsByIds(normalize_ids(endpoint_ids));
match self
.get_or_load(key, || async move {
self.inner
.list_endpoints_by_ids(endpoint_ids)
.await
.map(ProviderCatalogCacheValue::Endpoints)
})
.await?
{
ProviderCatalogCacheValue::Endpoints(items) => Ok(items),
_ => Ok(Vec::new()),
}
}
async fn list_endpoints_by_provider_ids(
@@ -200,7 +212,19 @@ impl ProviderCatalogReadRepository for CachedProviderCatalogReadRepository {
&self,
key_ids: &[String],
) -> Result<Vec<StoredProviderCatalogKey>, DataLayerError> {
self.inner.list_keys_by_ids(key_ids).await
let key = ProviderCatalogCacheKey::KeysByIds(normalize_ids(key_ids));
match self
.get_or_load(key, || async move {
self.inner
.list_keys_by_ids(key_ids)
.await
.map(ProviderCatalogCacheValue::Keys)
})
.await?
{
ProviderCatalogCacheValue::Keys(items) => Ok(items),
_ => Ok(Vec::new()),
}
}
async fn list_keys_by_provider_ids(
@@ -293,7 +317,9 @@ impl ProviderCatalogReadRepository for CachedProviderCatalogReadRepository {
enum ProviderCatalogCacheKey {
Providers { active_only: bool },
ProvidersByIds(Vec<String>),
EndpointsByIds(Vec<String>),
EndpointsByProviderIds(Vec<String>),
KeysByIds(Vec<String>),
KeysByProviderIds(Vec<String>),
KeySummariesByProviderIds(Vec<String>),
KeyMaintenanceSummariesByProviderIds(Vec<String>),
@@ -0,0 +1,143 @@
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
use aether_data_contracts::repository::candidates::{
PublicHealthStatusCount, PublicHealthTimelineBucket, RequestCandidateReadRepository,
StoredRequestCandidate,
};
use async_trait::async_trait;
const RECENT_REQUEST_CANDIDATES_TTL: Duration = Duration::from_millis(100);
const RECENT_REQUEST_CANDIDATES_MAX_CACHE_KEYS: usize = 8;
#[derive(Clone)]
pub(super) struct CachedRequestCandidateReadRepository {
inner: Arc<dyn RequestCandidateReadRepository>,
recent: Arc<RwLock<BTreeMap<usize, (Instant, Vec<StoredRequestCandidate>)>>>,
recent_load_guard: Arc<tokio::sync::Mutex<()>>,
}
impl CachedRequestCandidateReadRepository {
pub(super) fn new(inner: Arc<dyn RequestCandidateReadRepository>) -> Self {
Self {
inner,
recent: Default::default(),
recent_load_guard: Default::default(),
}
}
fn cached_recent(&self, limit: usize, now: Instant) -> Option<Vec<StoredRequestCandidate>> {
self.recent
.read()
.expect("recent request candidates cache lock")
.get(&limit)
.filter(|(loaded_at, _)| {
now.duration_since(*loaded_at) <= RECENT_REQUEST_CANDIDATES_TTL
})
.map(|(_, rows)| rows.clone())
}
fn store_recent(&self, limit: usize, rows: &[StoredRequestCandidate], now: Instant) {
let mut cache = self
.recent
.write()
.expect("recent request candidates cache lock");
if cache.len() >= RECENT_REQUEST_CANDIDATES_MAX_CACHE_KEYS && !cache.contains_key(&limit) {
if let Some(oldest_key) = cache
.iter()
.min_by_key(|(_, (loaded_at, _))| *loaded_at)
.map(|(key, _)| *key)
{
cache.remove(&oldest_key);
}
}
cache.insert(limit, (now, rows.to_vec()));
}
}
#[async_trait]
impl RequestCandidateReadRepository for CachedRequestCandidateReadRepository {
async fn list_by_request_id(
&self,
request_id: &str,
) -> Result<Vec<StoredRequestCandidate>, aether_data::DataLayerError> {
self.inner.list_by_request_id(request_id).await
}
async fn list_attempted_by_request_id(
&self,
request_id: &str,
) -> Result<Vec<StoredRequestCandidate>, aether_data::DataLayerError> {
self.inner.list_attempted_by_request_id(request_id).await
}
async fn list_recent(
&self,
limit: usize,
) -> Result<Vec<StoredRequestCandidate>, aether_data::DataLayerError> {
if limit == 0 {
return Ok(Vec::new());
}
let now = Instant::now();
if let Some(rows) = self.cached_recent(limit, now) {
return Ok(rows);
}
let _guard = self.recent_load_guard.lock().await;
let now = Instant::now();
if let Some(rows) = self.cached_recent(limit, now) {
return Ok(rows);
}
let rows = self.inner.list_recent(limit).await?;
self.store_recent(limit, &rows, now);
Ok(rows)
}
async fn list_by_provider_id(
&self,
provider_id: &str,
limit: usize,
) -> Result<Vec<StoredRequestCandidate>, aether_data::DataLayerError> {
self.inner.list_by_provider_id(provider_id, limit).await
}
async fn list_finalized_by_endpoint_ids_since(
&self,
endpoint_ids: &[String],
since_unix_secs: u64,
limit: usize,
) -> Result<Vec<StoredRequestCandidate>, aether_data::DataLayerError> {
self.inner
.list_finalized_by_endpoint_ids_since(endpoint_ids, since_unix_secs, limit)
.await
}
async fn count_finalized_statuses_by_endpoint_ids_since(
&self,
endpoint_ids: &[String],
since_unix_secs: u64,
) -> Result<Vec<PublicHealthStatusCount>, aether_data::DataLayerError> {
self.inner
.count_finalized_statuses_by_endpoint_ids_since(endpoint_ids, since_unix_secs)
.await
}
async fn aggregate_finalized_timeline_by_endpoint_ids_since(
&self,
endpoint_ids: &[String],
since_unix_secs: u64,
until_unix_secs: u64,
segments: u32,
) -> Result<Vec<PublicHealthTimelineBucket>, aether_data::DataLayerError> {
self.inner
.aggregate_finalized_timeline_by_endpoint_ids_since(
endpoint_ids,
since_unix_secs,
until_unix_secs,
segments,
)
.await
}
}
@@ -0,0 +1,170 @@
use std::sync::Arc;
use std::time::Duration;
use aether_cache::ExpiringMap;
use aether_data::DataLayerError;
use aether_data_contracts::repository::routing_profiles::{
RoutingGroupBindingQuery, RoutingGroupBindingSubject, RoutingGroupLookupKey,
RoutingGroupReadRepository, StoredRoutingGroup, StoredRoutingGroupBinding,
StoredRoutingGroupVersion,
};
use async_trait::async_trait;
const ROUTING_GROUP_CACHE_TTL: Duration = Duration::from_secs(5);
const ROUTING_GROUP_CACHE_MAX_ENTRIES: usize = 4_096;
pub(super) struct CachedRoutingGroupReadRepository {
inner: Arc<dyn RoutingGroupReadRepository>,
entries: ExpiringMap<RoutingGroupCacheKey, RoutingGroupCacheValue>,
load_guard: tokio::sync::Mutex<()>,
}
impl CachedRoutingGroupReadRepository {
pub(super) fn new(inner: Arc<dyn RoutingGroupReadRepository>) -> Self {
Self {
inner,
entries: ExpiringMap::new(),
load_guard: tokio::sync::Mutex::new(()),
}
}
async fn get_or_load(
&self,
key: RoutingGroupCacheKey,
load: impl std::future::Future<Output = Result<RoutingGroupCacheValue, DataLayerError>>,
) -> Result<RoutingGroupCacheValue, DataLayerError> {
if let Some(value) = self.entries.get_fresh(&key, ROUTING_GROUP_CACHE_TTL) {
return Ok(value);
}
let _guard = self.load_guard.lock().await;
if let Some(value) = self.entries.get_fresh(&key, ROUTING_GROUP_CACHE_TTL) {
return Ok(value);
}
let value = load.await?;
self.entries.insert(
key,
value.clone(),
ROUTING_GROUP_CACHE_TTL,
ROUTING_GROUP_CACHE_MAX_ENTRIES,
);
Ok(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum RoutingGroupCacheKey {
ListGroups,
FindById(String),
FindByName(String),
FindSystemDefault,
Bindings {
group_id: Option<String>,
subject_type: Option<&'static str>,
subject_id: Option<String>,
},
Versions(String),
}
#[derive(Debug, Clone)]
enum RoutingGroupCacheValue {
Groups(Vec<StoredRoutingGroup>),
Group(Option<StoredRoutingGroup>),
Bindings(Vec<StoredRoutingGroupBinding>),
Versions(Vec<StoredRoutingGroupVersion>),
}
fn lookup_cache_key(lookup: &RoutingGroupLookupKey<'_>) -> RoutingGroupCacheKey {
match lookup {
RoutingGroupLookupKey::Id(id) => RoutingGroupCacheKey::FindById((*id).to_string()),
RoutingGroupLookupKey::Name(name) => RoutingGroupCacheKey::FindByName((*name).to_string()),
RoutingGroupLookupKey::SystemDefault => RoutingGroupCacheKey::FindSystemDefault,
}
}
fn subject_cache_key(subject: Option<RoutingGroupBindingSubject>) -> Option<&'static str> {
match subject {
Some(RoutingGroupBindingSubject::User) => Some("user"),
Some(RoutingGroupBindingSubject::ApiKey) => Some("api_key"),
Some(RoutingGroupBindingSubject::UserGroup) => Some("user_group"),
None => None,
}
}
#[async_trait]
impl RoutingGroupReadRepository for CachedRoutingGroupReadRepository {
async fn list_routing_groups(&self) -> Result<Vec<StoredRoutingGroup>, DataLayerError> {
match self
.get_or_load(RoutingGroupCacheKey::ListGroups, async {
self.inner
.list_routing_groups()
.await
.map(RoutingGroupCacheValue::Groups)
})
.await?
{
RoutingGroupCacheValue::Groups(groups) => Ok(groups),
_ => Ok(Vec::new()),
}
}
async fn find_routing_group(
&self,
lookup: RoutingGroupLookupKey<'_>,
) -> Result<Option<StoredRoutingGroup>, DataLayerError> {
let key = lookup_cache_key(&lookup);
match self
.get_or_load(key, async {
self.inner
.find_routing_group(lookup)
.await
.map(RoutingGroupCacheValue::Group)
})
.await?
{
RoutingGroupCacheValue::Group(group) => Ok(group),
_ => Ok(None),
}
}
async fn list_routing_group_bindings(
&self,
query: &RoutingGroupBindingQuery,
) -> Result<Vec<StoredRoutingGroupBinding>, DataLayerError> {
let key = RoutingGroupCacheKey::Bindings {
group_id: query.group_id.clone(),
subject_type: subject_cache_key(query.subject_type),
subject_id: query.subject_id.clone(),
};
match self
.get_or_load(key, async {
self.inner
.list_routing_group_bindings(query)
.await
.map(RoutingGroupCacheValue::Bindings)
})
.await?
{
RoutingGroupCacheValue::Bindings(bindings) => Ok(bindings),
_ => Ok(Vec::new()),
}
}
async fn list_routing_group_versions(
&self,
group_id: &str,
) -> Result<Vec<StoredRoutingGroupVersion>, DataLayerError> {
let key = RoutingGroupCacheKey::Versions(group_id.to_string());
match self
.get_or_load(key, async {
self.inner
.list_routing_group_versions(group_id)
.await
.map(RoutingGroupCacheValue::Versions)
})
.await?
{
RoutingGroupCacheValue::Versions(versions) => Ok(versions),
_ => Ok(Vec::new()),
}
}
}
+144 -21
View File
@@ -5,8 +5,8 @@ use super::{
AdminBillingRuleWriteInput, AdminPaymentOrderListQuery, AdminRedeemCodeBatchListQuery,
AdminRedeemCodeListQuery, AdminWalletLedgerQuery, AdminWalletListQuery,
AdminWalletRefundRequestListQuery, AnnouncementListQuery, AuditLogListQuery,
BackgroundTaskListQuery, BackgroundTaskSummary, BillingPlanRecord, BillingPlanWriteInput,
CompleteAdminWalletRefundInput, CreateAdminRedeemCodeBatchInput,
BackgroundTaskListQuery, BackgroundTaskSummary, BillingModelContextCacheKey, BillingPlanRecord,
BillingPlanWriteInput, CompleteAdminWalletRefundInput, CreateAdminRedeemCodeBatchInput,
CreateAdminRedeemCodeBatchResult, CreateAnnouncementRecord, CreateManualWalletRechargeInput,
CreatePlanPurchaseOrderInput, CreatePlanPurchaseOrderOutcome, CreateWalletRechargeOrderInput,
CreateWalletRechargeOrderOutcome, CreateWalletRefundRequestInput,
@@ -44,9 +44,23 @@ use aether_runtime_state::RuntimeQueueStore;
use aether_video_tasks_core::read_data_backed_video_task_response;
use std::time::{Duration, Instant};
fn normalize_billing_context_cache_part(value: &str) -> String {
value.trim().to_string()
}
fn normalize_optional_billing_context_cache_part(value: Option<&str>) -> Option<String> {
value
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
}
impl GatewayDataState {
const MAINTENANCE_POOL_IDLE_RESERVE: usize = 1;
const MAINTENANCE_POOL_IDLE_RESERVE_ENV: &'static str =
"AETHER_GATEWAY_MAINTENANCE_POOL_IDLE_RESERVE";
const MAINTENANCE_POOL_PRESSURE_MAX_DEFER: Duration = Duration::from_secs(30);
const BILLING_MODEL_CONTEXT_CACHE_TTL: Duration = Duration::from_secs(30);
const BILLING_MODEL_CONTEXT_CACHE_MAX_ENTRIES: usize = 4096;
pub(crate) async fn run_database_maintenance(
&self,
@@ -125,7 +139,26 @@ impl GatewayDataState {
pub(crate) fn database_pool_summary_under_maintenance_pressure(
summary: &aether_data::DatabasePoolSummary,
) -> bool {
summary.checked_out > 0 && summary.idle <= Self::MAINTENANCE_POOL_IDLE_RESERVE
summary.checked_out > 0 && summary.idle <= Self::maintenance_pool_idle_reserve(summary)
}
pub(crate) fn maintenance_pool_idle_reserve(
summary: &aether_data::DatabasePoolSummary,
) -> usize {
if let Some(override_value) = std::env::var(Self::MAINTENANCE_POOL_IDLE_RESERVE_ENV)
.ok()
.and_then(|value| value.trim().parse::<usize>().ok())
{
return override_value;
}
let max_connections = summary.max_connections as usize;
if max_connections == 0 {
return 0;
}
let ten_percent_ceil = (max_connections + 9) / 10;
ten_percent_ceil.clamp(2, 10).min(max_connections)
}
pub(crate) fn should_defer_maintenance_for_database_pool_pressure(
@@ -1615,15 +1648,14 @@ impl GatewayDataState {
&self,
user_id: &str,
) -> Result<Option<serde_json::Value>, DataLayerError> {
let user_id = user_id.trim();
if user_id.is_empty() {
return Ok(None);
}
if let Some(user) = self.find_export_user_by_id(user_id).await? {
return Ok(user.feature_settings);
}
Ok(self
.list_non_admin_export_users()
.await?
.into_iter()
.find(|user| user.id == user_id)
.and_then(|user| user.feature_settings))
Ok(None)
}
pub(crate) async fn list_non_admin_export_users(
@@ -1670,13 +1702,26 @@ impl GatewayDataState {
provider_api_key_id: Option<&str>,
global_model_name: &str,
) -> Result<Option<StoredBillingModelContext>, DataLayerError> {
let key = BillingModelContextCacheKey::ByGlobalModelName {
provider_id: normalize_billing_context_cache_part(provider_id),
provider_api_key_id: normalize_optional_billing_context_cache_part(provider_api_key_id),
global_model_name: normalize_billing_context_cache_part(global_model_name),
};
if let Some(value) = self.cached_billing_model_context(&key) {
return Ok(value);
}
match &self.billing_reader {
Some(repository) => {
repository
let value = repository
.find_model_context(provider_id, provider_api_key_id, global_model_name)
.await
.await?;
self.remember_billing_model_context(key, value.clone());
Ok(value)
}
None => {
self.remember_billing_model_context(key, None);
Ok(None)
}
None => Ok(None),
}
}
@@ -1686,16 +1731,74 @@ impl GatewayDataState {
provider_api_key_id: Option<&str>,
model_id: &str,
) -> Result<Option<StoredBillingModelContext>, DataLayerError> {
let key = BillingModelContextCacheKey::ByModelId {
provider_id: normalize_billing_context_cache_part(provider_id),
provider_api_key_id: normalize_optional_billing_context_cache_part(provider_api_key_id),
model_id: normalize_billing_context_cache_part(model_id),
};
if let Some(value) = self.cached_billing_model_context(&key) {
return Ok(value);
}
match &self.billing_reader {
Some(repository) => {
repository
let value = repository
.find_model_context_by_model_id(provider_id, provider_api_key_id, model_id)
.await
.await?;
self.remember_billing_model_context(key, value.clone());
Ok(value)
}
None => {
self.remember_billing_model_context(key, None);
Ok(None)
}
None => Ok(None),
}
}
fn cached_billing_model_context(
&self,
key: &BillingModelContextCacheKey,
) -> Option<Option<StoredBillingModelContext>> {
self.billing_model_context_cache
.read()
.expect("billing model context cache lock")
.get(key)
.and_then(|(cached_at, value)| {
(cached_at.elapsed() <= Self::BILLING_MODEL_CONTEXT_CACHE_TTL)
.then(|| value.clone())
})
}
fn remember_billing_model_context(
&self,
key: BillingModelContextCacheKey,
value: Option<StoredBillingModelContext>,
) {
let mut cache = self
.billing_model_context_cache
.write()
.expect("billing model context cache lock");
cache.retain(|_, (cached_at, _)| {
cached_at.elapsed() <= Self::BILLING_MODEL_CONTEXT_CACHE_TTL
});
if cache.len() >= Self::BILLING_MODEL_CONTEXT_CACHE_MAX_ENTRIES {
if let Some(oldest_key) = cache
.iter()
.min_by_key(|(_, (cached_at, _))| *cached_at)
.map(|(key, _)| key.clone())
{
cache.remove(&oldest_key);
}
}
cache.insert(key, (Instant::now(), value));
}
fn clear_billing_model_context_cache(&self) {
self.billing_model_context_cache
.write()
.expect("billing model context cache lock")
.clear();
}
pub(crate) async fn admin_billing_enabled_default_value_exists(
&self,
api_format: &str,
@@ -1722,10 +1825,14 @@ impl GatewayDataState {
&self,
input: &AdminBillingRuleWriteInput,
) -> Result<AdminBillingMutationOutcome<AdminBillingRuleRecord>, DataLayerError> {
match &self.billing_reader {
let result = match &self.billing_reader {
Some(repository) => repository.create_admin_billing_rule(input).await,
None => Ok(AdminBillingMutationOutcome::Unavailable),
};
if result.is_ok() {
self.clear_billing_model_context_cache();
}
result
}
pub(crate) async fn list_admin_billing_rules(
@@ -1760,20 +1867,28 @@ impl GatewayDataState {
rule_id: &str,
input: &AdminBillingRuleWriteInput,
) -> Result<AdminBillingMutationOutcome<AdminBillingRuleRecord>, DataLayerError> {
match &self.billing_reader {
let result = match &self.billing_reader {
Some(repository) => repository.update_admin_billing_rule(rule_id, input).await,
None => Ok(AdminBillingMutationOutcome::Unavailable),
};
if result.is_ok() {
self.clear_billing_model_context_cache();
}
result
}
pub(crate) async fn create_admin_billing_collector(
&self,
input: &AdminBillingCollectorWriteInput,
) -> Result<AdminBillingMutationOutcome<AdminBillingCollectorRecord>, DataLayerError> {
match &self.billing_reader {
let result = match &self.billing_reader {
Some(repository) => repository.create_admin_billing_collector(input).await,
None => Ok(AdminBillingMutationOutcome::Unavailable),
};
if result.is_ok() {
self.clear_billing_model_context_cache();
}
result
}
pub(crate) async fn list_admin_billing_collectors(
@@ -1817,14 +1932,18 @@ impl GatewayDataState {
collector_id: &str,
input: &AdminBillingCollectorWriteInput,
) -> Result<AdminBillingMutationOutcome<AdminBillingCollectorRecord>, DataLayerError> {
match &self.billing_reader {
let result = match &self.billing_reader {
Some(repository) => {
repository
.update_admin_billing_collector(collector_id, input)
.await
}
None => Ok(AdminBillingMutationOutcome::Unavailable),
};
if result.is_ok() {
self.clear_billing_model_context_cache();
}
result
}
pub(crate) async fn apply_admin_billing_preset(
@@ -1833,14 +1952,18 @@ impl GatewayDataState {
mode: &str,
collectors: &[AdminBillingCollectorWriteInput],
) -> Result<AdminBillingMutationOutcome<AdminBillingPresetApplyResult>, DataLayerError> {
match &self.billing_reader {
let result = match &self.billing_reader {
Some(repository) => {
repository
.apply_admin_billing_preset(preset, mode, collectors)
.await
}
None => Ok(AdminBillingMutationOutcome::Unavailable),
};
if result.is_ok() {
self.clear_billing_model_context_cache();
}
result
}
pub(crate) async fn find_payment_gateway_config(
@@ -53,6 +53,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -109,6 +111,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
}
@@ -89,6 +89,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -144,6 +146,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -195,6 +199,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -346,6 +352,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -425,6 +433,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -485,6 +495,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -554,6 +566,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -605,6 +619,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -657,6 +673,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -720,6 +738,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -785,6 +805,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -834,6 +856,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -898,6 +922,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -955,6 +981,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1013,6 +1041,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1075,6 +1105,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1138,6 +1170,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1200,6 +1234,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1251,6 +1287,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1302,6 +1340,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1365,6 +1405,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1433,6 +1475,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1484,6 +1528,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1540,6 +1586,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1613,6 +1661,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1681,6 +1731,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1733,6 +1785,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1785,6 +1839,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1839,6 +1895,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1891,6 +1949,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1943,6 +2003,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -1995,6 +2057,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2055,6 +2119,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2116,6 +2182,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2180,6 +2248,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2250,6 +2320,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2321,6 +2393,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2396,6 +2470,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2478,6 +2554,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2542,6 +2620,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2597,6 +2677,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2648,6 +2730,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2705,6 +2789,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2766,6 +2852,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2828,6 +2916,8 @@ impl GatewayDataState {
wallet_writer: Some(wallet_writer),
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -2883,6 +2973,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
}
@@ -57,6 +57,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -115,6 +117,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -170,6 +174,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -229,6 +235,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -292,6 +300,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
@@ -364,6 +374,8 @@ impl GatewayDataState {
wallet_writer: None,
settlement_writer: None,
system_config_values: None,
system_config_value_cache: Default::default(),
billing_model_context_cache: Default::default(),
}
}
}