mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-06 01:17:46 +08:00
671 lines
22 KiB
Rust
671 lines
22 KiB
Rust
use std::time::Duration;
|
|
|
|
use aether_contracts::{ExecutionPlan, ExecutionResult, ProxySnapshot};
|
|
use aether_data_contracts::repository::candidates::{
|
|
StoredRequestCandidate, UpsertRequestCandidateRecord,
|
|
};
|
|
use aether_data_contracts::repository::global_models::{
|
|
AdminGlobalModelListQuery, AdminProviderModelListQuery, StoredAdminGlobalModelPage,
|
|
StoredAdminProviderModel, UpsertAdminProviderModelRecord,
|
|
};
|
|
use aether_data_contracts::repository::provider_catalog::{
|
|
ProviderCatalogUpstreamMetadataNamespaceUpdate, StoredProviderCatalogEndpoint,
|
|
StoredProviderCatalogKey, StoredProviderCatalogProvider,
|
|
};
|
|
use aether_data_contracts::repository::quota::StoredProviderQuotaSnapshot;
|
|
use aether_model_fetch::{
|
|
aggregate_models_for_cache, build_antigravity_load_code_assist_plan,
|
|
fetch_models_from_transports, merge_upstream_metadata, model_fetch_interval_minutes,
|
|
ModelFetchAssociationStore, ModelFetchTransportRuntime,
|
|
};
|
|
use aether_scheduler_core::SchedulerAffinityTarget;
|
|
use async_trait::async_trait;
|
|
use serde_json::Value;
|
|
use tracing::{debug, warn};
|
|
|
|
use super::{AppState, GatewayError};
|
|
use crate::clock::current_unix_secs;
|
|
use crate::model_fetch::{CodexCatalogRuntime, ModelFetchRuntimeState};
|
|
use crate::provider_transport::{GatewayProviderTransportSnapshot, LocalResolvedOAuthRequestAuth};
|
|
use crate::request_candidate_runtime::{
|
|
RequestCandidateRuntimeCapabilityReader, RequestCandidateRuntimeReader,
|
|
RequestCandidateRuntimeWriter,
|
|
};
|
|
use crate::scheduler::state::SchedulerRuntimeState;
|
|
use crate::{execution_runtime, provider_transport};
|
|
|
|
const MODEL_FETCH_RESPONSE_BODY_LIMIT_BYTES: usize = 8 * 1024 * 1024;
|
|
|
|
impl AppState {
|
|
pub(crate) async fn hydrate_antigravity_project_metadata_for_transport(
|
|
&self,
|
|
transport: &GatewayProviderTransportSnapshot,
|
|
) -> Option<GatewayProviderTransportSnapshot> {
|
|
if !provider_transport::antigravity::is_antigravity_provider_transport(transport) {
|
|
return None;
|
|
}
|
|
if matches!(
|
|
provider_transport::antigravity::resolve_local_antigravity_request_auth(transport),
|
|
provider_transport::antigravity::AntigravityRequestAuthSupport::Supported(_)
|
|
) {
|
|
return Some(transport.clone());
|
|
}
|
|
|
|
let plan = match build_antigravity_load_code_assist_plan(self, transport).await {
|
|
Ok(plan) => plan,
|
|
Err(err) => {
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
error = %err,
|
|
"antigravity project metadata hydration failed"
|
|
);
|
|
return None;
|
|
}
|
|
};
|
|
let result =
|
|
match execution_runtime::execute_execution_runtime_sync_plan(self, None, &plan).await {
|
|
Ok(result) => result,
|
|
Err(err) => {
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
error = ?err,
|
|
"antigravity project metadata hydration request failed"
|
|
);
|
|
return None;
|
|
}
|
|
};
|
|
if !(200..300).contains(&result.status_code) {
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
status_code = result.status_code,
|
|
"antigravity project metadata hydration returned non-success status"
|
|
);
|
|
return None;
|
|
}
|
|
let Some(project_id) = result
|
|
.body
|
|
.as_ref()
|
|
.and_then(|body| body.json_body.as_ref())
|
|
.and_then(extract_antigravity_load_code_assist_project_id)
|
|
else {
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
"antigravity project metadata hydration response missing project"
|
|
);
|
|
return None;
|
|
};
|
|
let upstream_metadata = serde_json::json!({
|
|
"antigravity": {
|
|
"project_id": project_id,
|
|
"updated_at": current_unix_secs(),
|
|
}
|
|
});
|
|
let merged_metadata =
|
|
merge_upstream_metadata(transport.key.upstream_metadata.as_ref(), &upstream_metadata);
|
|
|
|
let mut hydrated = transport.clone();
|
|
hydrated.key.upstream_metadata = Some(merged_metadata.clone());
|
|
if !matches!(
|
|
provider_transport::antigravity::resolve_local_antigravity_request_auth(&hydrated),
|
|
provider_transport::antigravity::AntigravityRequestAuthSupport::Supported(_)
|
|
) {
|
|
return None;
|
|
}
|
|
|
|
if let Err(err) = self
|
|
.update_provider_catalog_key_upstream_metadata(
|
|
&transport.key.id,
|
|
Some(&merged_metadata),
|
|
Some(current_unix_secs()),
|
|
)
|
|
.await
|
|
{
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
error = ?err,
|
|
"antigravity project metadata hydration could not persist metadata"
|
|
);
|
|
}
|
|
|
|
Some(hydrated)
|
|
}
|
|
|
|
pub(crate) async fn hydrate_gemini_cli_project_metadata_for_transport(
|
|
&self,
|
|
transport: &GatewayProviderTransportSnapshot,
|
|
) -> Option<GatewayProviderTransportSnapshot> {
|
|
if !provider_transport::is_gemini_cli_provider_transport(transport) {
|
|
return None;
|
|
}
|
|
if provider_transport::resolve_gemini_cli_project_id(transport).is_some() {
|
|
return Some(transport.clone());
|
|
}
|
|
|
|
let outcome =
|
|
match fetch_models_from_transports(self, std::slice::from_ref(transport)).await {
|
|
Ok(outcome) => outcome,
|
|
Err(err) => {
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
error = %err,
|
|
"gemini_cli project metadata hydration failed"
|
|
);
|
|
return None;
|
|
}
|
|
};
|
|
let upstream_metadata = outcome.upstream_metadata.as_ref()?;
|
|
let merged_metadata =
|
|
merge_upstream_metadata(transport.key.upstream_metadata.as_ref(), upstream_metadata);
|
|
|
|
let mut hydrated = transport.clone();
|
|
hydrated.key.upstream_metadata = Some(merged_metadata.clone());
|
|
if provider_transport::resolve_gemini_cli_project_id(&hydrated).is_none() {
|
|
return None;
|
|
}
|
|
|
|
if let Err(err) = self
|
|
.update_provider_catalog_key_upstream_metadata(
|
|
&transport.key.id,
|
|
Some(&merged_metadata),
|
|
Some(current_unix_secs()),
|
|
)
|
|
.await
|
|
{
|
|
warn!(
|
|
provider_id = %transport.provider.id,
|
|
endpoint_id = %transport.endpoint.id,
|
|
key_id = %transport.key.id,
|
|
error = ?err,
|
|
"gemini_cli project metadata hydration could not persist metadata"
|
|
);
|
|
}
|
|
|
|
Some(hydrated)
|
|
}
|
|
}
|
|
|
|
fn extract_antigravity_load_code_assist_project_id(value: &Value) -> Option<String> {
|
|
let raw = value
|
|
.get("cloudaicompanionProject")
|
|
.or_else(|| value.get("cloudAiCompanionProject"))?;
|
|
if let Some(project_id) = raw
|
|
.as_str()
|
|
.map(str::trim)
|
|
.filter(|value| !value.is_empty())
|
|
{
|
|
return Some(project_id.to_string());
|
|
}
|
|
raw.as_object()
|
|
.and_then(|object| {
|
|
object
|
|
.get("id")
|
|
.or_else(|| object.get("project_id"))
|
|
.or_else(|| object.get("projectId"))
|
|
})
|
|
.and_then(Value::as_str)
|
|
.map(str::trim)
|
|
.filter(|value| !value.is_empty())
|
|
.map(ToOwned::to_owned)
|
|
}
|
|
|
|
#[async_trait]
|
|
impl provider_transport::TransportTunnelAffinityLookup for AppState {
|
|
async fn lookup_tunnel_attachment_owner(
|
|
&self,
|
|
node_id: &str,
|
|
) -> Result<Option<provider_transport::TransportTunnelAttachmentOwner>, String> {
|
|
self.tunnel
|
|
.lookup_attachment_owner(self.data.as_ref(), node_id)
|
|
.await
|
|
.map(|owner| {
|
|
owner.map(|owner| provider_transport::TransportTunnelAttachmentOwner {
|
|
gateway_instance_id: owner.gateway_instance_id,
|
|
relay_base_url: owner.relay_base_url,
|
|
observed_at_unix_secs: owner.observed_at_unix_secs,
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl provider_transport::VideoTaskTransportSnapshotLookup for AppState {
|
|
async fn read_video_task_provider_transport_snapshot(
|
|
&self,
|
|
provider_id: &str,
|
|
endpoint_id: &str,
|
|
key_id: &str,
|
|
) -> Result<Option<GatewayProviderTransportSnapshot>, String> {
|
|
self.read_provider_transport_snapshot(provider_id, endpoint_id, key_id)
|
|
.await
|
|
.map_err(GatewayError::into_message)
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ModelFetchTransportRuntime for AppState {
|
|
async fn resolve_local_oauth_request_auth(
|
|
&self,
|
|
transport: &GatewayProviderTransportSnapshot,
|
|
) -> Result<Option<LocalResolvedOAuthRequestAuth>, String> {
|
|
AppState::resolve_local_oauth_request_auth(self, transport)
|
|
.await
|
|
.map_err(GatewayError::into_message)
|
|
}
|
|
|
|
async fn resolve_model_fetch_proxy(
|
|
&self,
|
|
transport: &GatewayProviderTransportSnapshot,
|
|
) -> Option<ProxySnapshot> {
|
|
self.resolve_transport_proxy_snapshot_with_tunnel_affinity(transport)
|
|
.await
|
|
}
|
|
|
|
async fn execute_model_fetch_execution_plan(
|
|
&self,
|
|
plan: &ExecutionPlan,
|
|
) -> Result<ExecutionResult, String> {
|
|
let bounded_plan = execution_runtime::transport::with_upstream_response_body_limit(
|
|
plan,
|
|
MODEL_FETCH_RESPONSE_BODY_LIMIT_BYTES,
|
|
);
|
|
execution_runtime::execute_execution_runtime_sync_plan(self, None, &bounded_plan)
|
|
.await
|
|
.map_err(GatewayError::into_message)
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl CodexCatalogRuntime for AppState {
|
|
fn codex_catalog_runtime_state(&self) -> &aether_runtime_state::RuntimeState {
|
|
self.runtime_state.as_ref()
|
|
}
|
|
|
|
async fn read_codex_catalog_transport_snapshot(
|
|
&self,
|
|
provider_id: &str,
|
|
endpoint_id: &str,
|
|
key_id: &str,
|
|
) -> Result<Option<GatewayProviderTransportSnapshot>, String> {
|
|
self.read_provider_transport_snapshot(provider_id, endpoint_id, key_id)
|
|
.await
|
|
.map_err(GatewayError::into_message)
|
|
}
|
|
|
|
async fn read_codex_catalog_credential_scope_strong(
|
|
&self,
|
|
provider_id: &str,
|
|
key_id: &str,
|
|
) -> Result<Option<String>, String> {
|
|
let Some(key) = self
|
|
.list_provider_catalog_keys_by_ids_strong(&[key_id.to_string()])
|
|
.await
|
|
.map_err(GatewayError::into_message)?
|
|
.into_iter()
|
|
.find(|key| key.id == key_id && key.provider_id == provider_id && key.is_active)
|
|
else {
|
|
return Ok(None);
|
|
};
|
|
|
|
if let Some(scope) =
|
|
crate::model_fetch::codex_catalog_credential_scope_from_stored_key(&key, None, None)
|
|
{
|
|
return Ok(Some(scope));
|
|
}
|
|
|
|
let decrypted_auth_config = match key.encrypted_auth_config.as_deref() {
|
|
Some(ciphertext) => Some(
|
|
crate::handlers::shared::decrypt_catalog_secret_with_fallbacks(
|
|
self.encryption_key(),
|
|
ciphertext,
|
|
)
|
|
.ok_or_else(|| {
|
|
"Codex catalog auth config could not be verified for credential fencing"
|
|
.to_string()
|
|
})?,
|
|
),
|
|
None => None,
|
|
};
|
|
let decrypted_api_key = match key.encrypted_api_key.as_deref() {
|
|
Some(ciphertext) => Some(
|
|
crate::handlers::shared::decrypt_catalog_secret_with_fallbacks(
|
|
self.encryption_key(),
|
|
ciphertext,
|
|
)
|
|
.ok_or_else(|| {
|
|
"Codex catalog API key could not be verified for credential fencing".to_string()
|
|
})?,
|
|
),
|
|
None => None,
|
|
};
|
|
|
|
Ok(
|
|
crate::model_fetch::codex_catalog_credential_scope_from_stored_key(
|
|
&key,
|
|
decrypted_auth_config.as_deref(),
|
|
decrypted_api_key.as_deref(),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ModelFetchRuntimeState for AppState {
|
|
fn has_provider_catalog_data_reader(&self) -> bool {
|
|
AppState::has_provider_catalog_data_reader(self)
|
|
}
|
|
|
|
fn has_provider_catalog_data_writer(&self) -> bool {
|
|
AppState::has_provider_catalog_data_writer(self)
|
|
}
|
|
|
|
async fn list_provider_catalog_providers(
|
|
&self,
|
|
active_only: bool,
|
|
) -> Result<Vec<StoredProviderCatalogProvider>, GatewayError> {
|
|
AppState::list_provider_catalog_providers(self, active_only).await
|
|
}
|
|
|
|
async fn list_provider_catalog_endpoints_by_provider_ids(
|
|
&self,
|
|
provider_ids: &[String],
|
|
) -> Result<Vec<StoredProviderCatalogEndpoint>, GatewayError> {
|
|
AppState::list_provider_catalog_endpoints_by_provider_ids(self, provider_ids).await
|
|
}
|
|
|
|
async fn read_provider_transport_snapshot(
|
|
&self,
|
|
provider_id: &str,
|
|
endpoint_id: &str,
|
|
key_id: &str,
|
|
) -> Result<Option<GatewayProviderTransportSnapshot>, GatewayError> {
|
|
AppState::read_provider_transport_snapshot(self, provider_id, endpoint_id, key_id).await
|
|
}
|
|
|
|
async fn execute_execution_runtime_sync_plan(
|
|
&self,
|
|
plan: &ExecutionPlan,
|
|
) -> Result<ExecutionResult, GatewayError> {
|
|
execution_runtime::execute_execution_runtime_sync_plan(self, None, plan).await
|
|
}
|
|
|
|
async fn read_recent_codex_catalog_client_version(
|
|
&self,
|
|
provider_id: &str,
|
|
key_id: &str,
|
|
) -> Option<String> {
|
|
crate::model_fetch::read_codex_management_catalog(self, provider_id, key_id)
|
|
.await
|
|
.map(|catalog| catalog.client_version)
|
|
}
|
|
|
|
async fn update_provider_catalog_key_model_fetch_state(
|
|
&self,
|
|
key_id: &str,
|
|
allowed_models: Option<&Value>,
|
|
last_models_fetch_at_unix_secs: Option<u64>,
|
|
last_models_fetch_error: Option<&str>,
|
|
updated_at_unix_secs: Option<u64>,
|
|
) -> Result<(), GatewayError> {
|
|
AppState::update_provider_catalog_key_model_fetch_state(
|
|
self,
|
|
key_id,
|
|
allowed_models,
|
|
last_models_fetch_at_unix_secs,
|
|
last_models_fetch_error,
|
|
updated_at_unix_secs,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn update_provider_catalog_key_model_fetch_success(
|
|
&self,
|
|
key_id: &str,
|
|
allowed_models: Option<&Value>,
|
|
last_models_fetch_at_unix_secs: u64,
|
|
upstream_metadata_updates: &[ProviderCatalogUpstreamMetadataNamespaceUpdate],
|
|
updated_at_unix_secs: Option<u64>,
|
|
) -> Result<(), GatewayError> {
|
|
AppState::update_provider_catalog_key_model_fetch_success(
|
|
self,
|
|
key_id,
|
|
allowed_models,
|
|
last_models_fetch_at_unix_secs,
|
|
upstream_metadata_updates,
|
|
updated_at_unix_secs,
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn write_upstream_models_cache(
|
|
&self,
|
|
provider_id: &str,
|
|
key_id: &str,
|
|
cached_models: &[Value],
|
|
) {
|
|
let models = aggregate_models_for_cache(cached_models);
|
|
if models.is_empty() {
|
|
return;
|
|
}
|
|
let Ok(serialized) = serde_json::to_string(&models) else {
|
|
return;
|
|
};
|
|
let cache_key = format!("upstream_models:{provider_id}:{key_id}");
|
|
if let Err(err) = self
|
|
.runtime_state
|
|
.kv_set(
|
|
&cache_key,
|
|
serialized,
|
|
Some(std::time::Duration::from_secs(
|
|
model_fetch_interval_minutes().saturating_mul(60),
|
|
)),
|
|
)
|
|
.await
|
|
{
|
|
debug!(
|
|
provider_id = %provider_id,
|
|
key_id = %key_id,
|
|
error = %err,
|
|
"gateway model fetch cache write failed"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ModelFetchAssociationStore for AppState {
|
|
type Error = String;
|
|
|
|
fn has_global_model_reader(&self) -> bool {
|
|
self.data.has_global_model_reader()
|
|
}
|
|
|
|
fn has_global_model_writer(&self) -> bool {
|
|
self.data.has_global_model_writer()
|
|
}
|
|
|
|
fn model_fetch_internal_error(&self, message: String) -> Self::Error {
|
|
message
|
|
}
|
|
|
|
async fn list_admin_provider_models(
|
|
&self,
|
|
query: &AdminProviderModelListQuery,
|
|
) -> Result<Vec<StoredAdminProviderModel>, Self::Error> {
|
|
AppState::list_admin_provider_models(self, query)
|
|
.await
|
|
.map_err(|err| format!("{err:?}"))
|
|
}
|
|
|
|
async fn list_admin_global_models(
|
|
&self,
|
|
query: &AdminGlobalModelListQuery,
|
|
) -> Result<StoredAdminGlobalModelPage, Self::Error> {
|
|
AppState::list_admin_global_models(self, query)
|
|
.await
|
|
.map_err(|err| format!("{err:?}"))
|
|
}
|
|
|
|
async fn create_admin_provider_model(
|
|
&self,
|
|
record: &UpsertAdminProviderModelRecord,
|
|
) -> Result<Option<StoredAdminProviderModel>, Self::Error> {
|
|
AppState::create_admin_provider_model(self, record)
|
|
.await
|
|
.map_err(|err| format!("{err:?}"))
|
|
}
|
|
|
|
async fn list_provider_catalog_keys_by_provider_ids(
|
|
&self,
|
|
provider_ids: &[String],
|
|
) -> Result<Vec<StoredProviderCatalogKey>, Self::Error> {
|
|
AppState::list_provider_catalog_keys_by_provider_ids(self, provider_ids)
|
|
.await
|
|
.map_err(|err| format!("{err:?}"))
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl RequestCandidateRuntimeReader for AppState {
|
|
async fn read_request_candidates_by_request_id(
|
|
&self,
|
|
request_id: &str,
|
|
) -> Result<Vec<StoredRequestCandidate>, GatewayError> {
|
|
AppState::read_request_candidates_by_request_id(self, request_id).await
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl RequestCandidateRuntimeCapabilityReader for AppState {
|
|
async fn read_request_candidate_user_model_capability_settings(
|
|
&self,
|
|
user_id: &str,
|
|
) -> Result<Option<Value>, GatewayError> {
|
|
AppState::read_user_model_capability_settings(self, user_id).await
|
|
}
|
|
|
|
async fn read_request_candidate_api_key_force_capabilities(
|
|
&self,
|
|
user_id: &str,
|
|
api_key_id: &str,
|
|
) -> Result<Option<Value>, GatewayError> {
|
|
AppState::read_auth_api_key_force_capabilities(self, user_id, api_key_id).await
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl RequestCandidateRuntimeWriter for AppState {
|
|
fn has_request_candidate_data_writer(&self) -> bool {
|
|
AppState::has_request_candidate_data_writer(self)
|
|
}
|
|
|
|
async fn upsert_request_candidate(
|
|
&self,
|
|
candidate: UpsertRequestCandidateRecord,
|
|
) -> Result<Option<StoredRequestCandidate>, GatewayError> {
|
|
AppState::upsert_request_candidate(self, candidate).await
|
|
}
|
|
|
|
async fn enqueue_request_candidate_status(
|
|
&self,
|
|
candidate: UpsertRequestCandidateRecord,
|
|
) -> Result<Option<()>, GatewayError> {
|
|
AppState::enqueue_request_candidate_status(self, candidate).await
|
|
}
|
|
|
|
fn try_enqueue_request_candidate_status(
|
|
&self,
|
|
candidate: UpsertRequestCandidateRecord,
|
|
) -> Result<(), UpsertRequestCandidateRecord> {
|
|
AppState::try_enqueue_request_candidate_status(self, candidate)
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl SchedulerRuntimeState for AppState {
|
|
async fn read_provider_quota_snapshot(
|
|
&self,
|
|
provider_id: &str,
|
|
) -> Result<Option<StoredProviderQuotaSnapshot>, GatewayError> {
|
|
AppState::read_provider_quota_snapshot(self, provider_id).await
|
|
}
|
|
|
|
async fn read_provider_catalog_providers_by_ids(
|
|
&self,
|
|
provider_ids: &[String],
|
|
) -> Result<Vec<StoredProviderCatalogProvider>, GatewayError> {
|
|
AppState::read_provider_catalog_providers_by_ids(self, provider_ids).await
|
|
}
|
|
|
|
async fn read_provider_catalog_keys_by_ids(
|
|
&self,
|
|
key_ids: &[String],
|
|
) -> Result<Vec<StoredProviderCatalogKey>, GatewayError> {
|
|
AppState::read_provider_catalog_keys_by_ids(self, key_ids).await
|
|
}
|
|
|
|
async fn read_recent_request_candidates(
|
|
&self,
|
|
limit: usize,
|
|
) -> Result<Vec<StoredRequestCandidate>, GatewayError> {
|
|
AppState::read_recent_request_candidates(self, limit).await
|
|
}
|
|
|
|
fn provider_key_rpm_reset_at(&self, key_id: &str, now_unix_secs: u64) -> Option<u64> {
|
|
AppState::provider_key_rpm_reset_at(self, key_id, now_unix_secs)
|
|
}
|
|
|
|
fn read_cached_scheduler_affinity_target(
|
|
&self,
|
|
cache_key: &str,
|
|
ttl: Duration,
|
|
) -> Option<SchedulerAffinityTarget> {
|
|
AppState::read_scheduler_affinity_target(self, cache_key, ttl)
|
|
}
|
|
|
|
fn scheduler_affinity_epoch(&self) -> u64 {
|
|
AppState::scheduler_affinity_epoch(self)
|
|
}
|
|
|
|
fn remember_scheduler_affinity_target(
|
|
&self,
|
|
cache_key: &str,
|
|
target: SchedulerAffinityTarget,
|
|
ttl: Duration,
|
|
max_entries: usize,
|
|
) {
|
|
AppState::remember_scheduler_affinity_target(self, cache_key, target, ttl, max_entries);
|
|
}
|
|
|
|
fn remember_scheduler_affinity_target_for_epoch(
|
|
&self,
|
|
cache_key: &str,
|
|
target: SchedulerAffinityTarget,
|
|
ttl: Duration,
|
|
max_entries: usize,
|
|
expected_epoch: Option<u64>,
|
|
) -> bool {
|
|
AppState::remember_scheduler_affinity_target_for_epoch(
|
|
self,
|
|
cache_key,
|
|
target,
|
|
ttl,
|
|
max_entries,
|
|
expected_epoch,
|
|
)
|
|
}
|
|
}
|