use crate::cache_file::write_collection_snapshot;
use crate::report::CollectionSourceRequest;
use crate::{
HostCacheError, QueryProgress,
cache::CacheCollectionCompleteness,
snapshot_cache::{
LockedSnapshotRefreshRequest, SnapshotEnvelope, SnapshotJsonPaths, SnapshotRefreshProgress,
publish_snapshot_with_attempt, run_snapshot_refresh_with_attempts,
with_locked_snapshot_refresh,
},
sns::report::{
SNS_CACHE_COMPONENT, SnsHostError, SnsReportContext, SnsSnapshotRefreshReport,
SnsSnapshotRefreshRequest,
cache_attempt::{
write_complete_sns_refresh_attempt, write_failed_sns_refresh_attempt,
write_starting_sns_refresh_attempt,
},
cache_paths::sns_snapshot_cache_paths,
cache_storage::SnsCacheMetadata,
live::LiveSnsSource,
neurons_cache::{
collection::fetch_complete_sns_neurons,
model::{CompleteSnsNeurons, SnsNeuronsCacheRows},
},
source::SnsNeuronsSource,
target::{resolve_neuron_target, validate_sns_refresh_page_size},
},
};
#[derive(Clone, Copy)]
pub(in crate::sns::report) struct SnsRefreshContext<'a> {
pub(in crate::sns::report) request: &'a SnsSnapshotRefreshRequest,
pub(in crate::sns::report) fetch_request: &'a CollectionSourceRequest,
pub(in crate::sns::report) governance_canister_id: &'a str,
pub(in crate::sns::report) paths: &'a SnapshotJsonPaths,
pub(in crate::sns::report) replaced_existing_cache: bool,
}
pub async fn refresh_sns_neurons_cache(
request: &SnsSnapshotRefreshRequest,
) -> Result<SnsSnapshotRefreshReport, SnsHostError> {
refresh_sns_neurons_cache_with_source_and_progress(
request,
&LiveSnsSource::default(),
&mut crate::progress::IgnoreQueryProgress,
)
.await
}
pub async fn refresh_sns_neurons_cache_with_source_and_progress(
request: &SnsSnapshotRefreshRequest,
source: &dyn SnsNeuronsSource,
progress: &mut dyn QueryProgress,
) -> Result<SnsSnapshotRefreshReport, SnsHostError> {
validate_sns_refresh_page_size(request.page_size)?;
let lookup = resolve_neuron_target(&request.target)?;
let paths = sns_snapshot_cache_paths(
&request.cache_root,
&request.target.network,
&lookup.governance_canister_id,
);
with_locked_snapshot_refresh(
LockedSnapshotRefreshRequest {
cache_root: &request.cache_root,
snapshot_path: &paths.snapshot_path,
refresh_lock_path: &paths.refresh_lock_path,
},
|error| SnsHostError::from(HostCacheError::operation(SNS_CACHE_COMPONENT, error)),
async |replaced_existing_snapshot| {
let context = SnsRefreshContext {
request,
fetch_request: &lookup.fetch_request,
governance_canister_id: &lookup.governance_canister_id,
paths: &paths,
replaced_existing_cache: replaced_existing_snapshot,
};
run_snapshot_refresh_with_attempts(
|| write_starting_sns_refresh_attempt(context),
async || {
let complete = fetch_complete_sns_neurons(context, source, progress).await?;
publish_complete_sns_snapshot(context, complete)
},
|error| write_failed_sns_refresh_attempt(context, error),
)
.await
},
)
.await
}
fn publish_complete_sns_snapshot(
context: SnsRefreshContext<'_>,
complete: CompleteSnsNeurons,
) -> Result<SnsSnapshotRefreshReport, SnsHostError> {
let page_count = complete.page_count;
let row_count = complete.rows.len();
let cache = SnapshotEnvelope {
schema_version: super::SNS_NEURONS_CACHE_SCHEMA_VERSION,
network: context.fetch_request.network.clone(),
fetched_at: context.fetch_request.fetched_at.clone(),
source_endpoint: context.fetch_request.endpoint.clone(),
fetched_by: context.fetch_request.fetched_by.clone(),
domain: "sns-neuron".to_string(),
entity: context.governance_canister_id.to_string(),
collection: "neurons".to_string(),
scope: "full".to_string(),
metadata: SnsCacheMetadata {
governance_canister_id: context.governance_canister_id.to_string(),
},
completeness: CacheCollectionCompleteness::api_exhausted(
context.request.page_size,
page_count,
row_count,
false,
),
data: SnsNeuronsCacheRows {
neurons: complete.rows,
},
};
let attempt_finalization_error = publish_snapshot_with_attempt(
|| {
write_collection_snapshot(
SNS_CACHE_COMPONENT,
&context.request.cache_root,
&context.paths.snapshot_path,
&cache,
)
.map_err(SnsHostError::from)
},
|| {
write_complete_sns_refresh_attempt(
context,
SnapshotRefreshProgress::new(page_count, row_count, complete.last_cursor),
)
},
)?;
let (report_context, _) = SnsReportContext::from_snapshot(cache);
Ok(SnsSnapshotRefreshReport {
schema_version: 1,
context: report_context,
collection: "neurons".to_string(),
cache_path: context.paths.snapshot_path.display().to_string(),
refresh_lock_path: context.paths.refresh_lock_path.display().to_string(),
refresh_attempt_path: context.paths.refresh_attempt_path.display().to_string(),
page_size: context.request.page_size,
page_count,
row_count,
complete: true,
replaced_existing_cache: context.replaced_existing_cache,
wrote_cache: true,
attempt_finalization_error,
})
}