arm构建

This commit is contained in:
xiuting.xu
2026-07-06 11:01:30 +08:00
parent cc3518897f
commit b5cbc7fbee
34 changed files with 1321 additions and 1055 deletions

115
src/rtr/bootstrap.rs Normal file
View File

@@ -0,0 +1,115 @@
use std::sync::Arc;
use std::time::Instant;
use anyhow::Result;
use arc_swap::ArcSwap;
use chrono::Utc;
use tracing::info;
use crate::rtr::cache::{RtrCache, SharedRtrCache};
use crate::rtr::config::AppConfig;
use crate::rtr::report::ReportContext;
use crate::rtr::server::{RtrService, RunningRtrService};
use crate::rtr::store::RtrStore;
use crate::source::pipeline::{PayloadLoadConfig, load_payloads_from_latest_sources_with_report};
pub fn open_store(config: &AppConfig) -> Result<RtrStore> {
info!("opening RTR store: {}", config.db_path);
RtrStore::open(&config.db_path)
}
pub fn init_shared_cache(
config: &AppConfig,
store: &RtrStore,
report_context: &ReportContext,
) -> Result<SharedRtrCache> {
let payload_load_config = PayloadLoadConfig {
ccr_dir: config.ccr_dir.clone(),
slurm_dir: config.slurm_dir.clone(),
};
let source_to_delta_started = Instant::now();
let report_context_for_loader = report_context.clone();
let initial_cache = RtrCache::default().init(
store,
config.max_delta,
config.prune_delta_by_snapshot_size,
config.timing,
|| {
let load = load_payloads_from_latest_sources_with_report(&payload_load_config)?;
info!(
"RTR source-to-delta timing: phase=startup_load_complete, ccr_dir={}, payload_count={}, elapsed_ms={}",
payload_load_config.ccr_dir,
load.payloads.len(),
source_to_delta_started.elapsed().as_millis()
);
report_context_for_loader.record_refresh_success(
Utc::now(),
source_to_delta_started.elapsed().as_millis(),
true,
load.source,
load.quality,
);
Ok(load.payloads)
},
)?;
info!(
"RTR source-to-delta timing: phase=startup_cache_init_complete, ccr_dir={}, serials={:?}, elapsed_ms={}",
payload_load_config.ccr_dir,
initial_cache.serials(),
source_to_delta_started.elapsed().as_millis()
);
let shared_cache: SharedRtrCache = Arc::new(ArcSwap::from_pointee(initial_cache));
let cache = shared_cache.load_full();
info!(
"cache initialized: session_ids={:?}, serials={:?}",
cache.session_ids(),
cache.serials()
);
Ok(shared_cache)
}
pub fn start_servers(config: &AppConfig, service: &RtrService) -> RunningRtrService {
if config.enable_tls && config.enable_ssh {
info!("starting TCP, TLS and SSH RTR servers");
service.spawn_tcp_tls_and_ssh_from_pem_and_openssh(
config.tcp_addr,
config.tls_addr,
config.ssh_addr,
&config.tls_cert_path,
&config.tls_key_path,
&config.tls_client_ca_path,
&config.ssh_host_key_path,
&config.ssh_authorized_keys_path,
&config.ssh_username,
&config.ssh_subsystem_name,
config.ssh_auth_mode,
config.ssh_password.as_deref(),
)
} else if config.enable_tls {
info!("starting TCP and TLS RTR servers");
service.spawn_tcp_and_tls_from_pem(
config.tcp_addr,
config.tls_addr,
&config.tls_cert_path,
&config.tls_key_path,
&config.tls_client_ca_path,
)
} else if config.enable_ssh {
info!("starting TCP and SSH RTR servers");
service.spawn_tcp_and_ssh_from_openssh(
config.tcp_addr,
config.ssh_addr,
&config.ssh_host_key_path,
&config.ssh_authorized_keys_path,
&config.ssh_username,
&config.ssh_subsystem_name,
config.ssh_auth_mode,
config.ssh_password.as_deref(),
)
} else {
info!("starting TCP RTR server");
service.spawn_tcp_only(config.tcp_addr)
}
}

View File

@@ -34,7 +34,6 @@ pub struct AppConfig {
pub max_delta: u8,
pub prune_delta_by_snapshot_size: bool,
pub strict_ccr_validation: bool,
pub source_refresh_interval: Duration,
pub report_dir: String,
pub runtime_report_interval: Duration,
@@ -54,7 +53,6 @@ pub struct RuntimeConfig {
pub source_refresh_interval_seconds: u64,
pub runtime_report_interval_seconds: u64,
pub report_history_limit: usize,
pub strict_ccr_validation: bool,
pub timezone: String,
pub timing: RuntimeTimingConfig,
}
@@ -73,7 +71,6 @@ pub struct RuntimeConfigPatch {
pub source_refresh_interval_seconds: Option<u64>,
pub runtime_report_interval_seconds: Option<u64>,
pub report_history_limit: Option<usize>,
pub strict_ccr_validation: Option<bool>,
pub timezone: Option<String>,
pub timing: Option<RuntimeTimingConfigPatch>,
}
@@ -109,7 +106,6 @@ impl Default for AppConfig {
max_delta: 100,
prune_delta_by_snapshot_size: false,
strict_ccr_validation: false,
source_refresh_interval: Duration::from_secs(300),
report_dir: "./report".to_string(),
runtime_report_interval: Duration::from_secs(300),
@@ -227,9 +223,6 @@ impl AppConfig {
config.prune_delta_by_snapshot_size =
parse_bool(&value, "RPKI_RTR_PRUNE_DELTA_BY_SNAPSHOT_SIZE")?;
}
if let Some(value) = env_var("RPKI_RTR_STRICT_CCR_VALIDATION")? {
config.strict_ccr_validation = parse_bool(&value, "RPKI_RTR_STRICT_CCR_VALIDATION")?;
}
if let Some(value) = env_var("RPKI_RTR_REPORT_DIR")? {
config.report_dir = value;
}
@@ -371,7 +364,6 @@ impl AppConfig {
source_refresh_interval_seconds: self.source_refresh_interval.as_secs(),
runtime_report_interval_seconds: self.runtime_report_interval.as_secs(),
report_history_limit: self.report_history_limit,
strict_ccr_validation: self.strict_ccr_validation,
timezone: format_timezone(self.timezone),
timing: RuntimeTimingConfig::from(self.timing),
}
@@ -417,9 +409,6 @@ impl RuntimeConfig {
}
next.report_history_limit = value;
}
if let Some(value) = patch.strict_ccr_validation {
next.strict_ccr_validation = value;
}
if let Some(value) = patch.timezone {
next.timezone = format_timezone(parse_timezone(&value, "timezone")?);
}
@@ -499,7 +488,7 @@ pub fn log_startup_config(config: &AppConfig) {
config.slurm_dir.as_deref().unwrap_or("disabled")
);
info!("max_delta={}", config.max_delta);
info!("strict_ccr_validation={}", config.strict_ccr_validation);
info!("strict_ccr_validation=true");
info!(
"source_refresh_interval_secs={}",
config.source_refresh_interval.as_secs()

View File

@@ -1,10 +1,12 @@
pub mod admin;
pub mod bootstrap;
pub mod cache;
pub mod config;
pub mod error_type;
pub mod payload;
pub mod pdu;
pub mod report;
pub mod runtime;
pub mod server;
pub mod session;
pub mod state;

View File

@@ -23,7 +23,6 @@ pub struct ReportConfiguration {
report_history_limit: usize,
max_delta: u8,
prune_delta_by_snapshot_size: bool,
strict_ccr_validation: bool,
timezone: String,
timing: TimingReport,
}
@@ -42,7 +41,6 @@ impl ReportConfiguration {
report_history_limit: usize,
max_delta: u8,
prune_delta_by_snapshot_size: bool,
strict_ccr_validation: bool,
timezone: Tz,
timing: (u32, u32, u32),
) -> Self {
@@ -52,7 +50,6 @@ impl ReportConfiguration {
report_history_limit,
max_delta,
prune_delta_by_snapshot_size,
strict_ccr_validation,
timezone: format_timezone(timezone),
timing: TimingReport {
refresh: timing.0,
@@ -169,7 +166,6 @@ impl ReportContext {
config.report_history_limit,
config.max_delta,
config.prune_delta_by_snapshot_size,
config.strict_ccr_validation,
timezone,
(
config.timing.refresh,

394
src/rtr/runtime.rs Normal file
View File

@@ -0,0 +1,394 @@
use std::path::PathBuf;
use std::time::Instant;
use anyhow::Result;
use chrono::Utc;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use tracing::{info, warn};
use crate::rtr::admin::{RuntimeConfigHandle, SourceReloadCommand, SourceReloadResult};
use crate::rtr::cache::{SharedRtrCache, Snapshot};
use crate::rtr::config::{AppConfig, RuntimeConfig};
use crate::rtr::report::{ReportContext, current_rss_mib};
use crate::rtr::server::{RtrNotifier, RtrServiceStats};
use crate::rtr::store::RtrStore;
use crate::source::pipeline::{
PayloadLoadConfig, SourceFingerprint, latest_sources_fingerprint,
load_payloads_from_latest_sources_with_report,
};
pub fn spawn_refresh_task(
config: &AppConfig,
runtime_config: RuntimeConfigHandle,
mut source_reload_rx: mpsc::Receiver<SourceReloadCommand>,
shared_cache: SharedRtrCache,
store: RtrStore,
notifier: RtrNotifier,
service_stats: RtrServiceStats,
report_context: ReportContext,
) -> JoinHandle<()> {
let report_dir = PathBuf::from(&config.report_dir);
let payload_load_config = PayloadLoadConfig {
ccr_dir: config.ccr_dir.clone(),
slurm_dir: config.slurm_dir.clone(),
};
let initial_runtime_config = runtime_config.current();
tokio::spawn(async move {
let mut active_runtime_config = initial_runtime_config;
let mut config_rx = runtime_config.subscribe();
let mut interval = tokio::time::interval(std::time::Duration::from_secs(
active_runtime_config.source_refresh_interval_seconds,
));
let mut last_fingerprint: Option<SourceFingerprint> = None;
report_context.write_source_or_warn(
&report_dir,
"startup",
&shared_cache,
&notifier,
&service_stats,
);
report_context.write_clients_or_warn(
&report_dir,
"startup",
&shared_cache,
&notifier,
&service_stats,
);
report_context.write_runtime_or_warn(
&report_dir,
"startup",
&shared_cache,
&notifier,
&service_stats,
);
let mut runtime_interval = tokio::time::interval_at(
tokio::time::Instant::now()
+ std::time::Duration::from_secs(
active_runtime_config.runtime_report_interval_seconds,
),
std::time::Duration::from_secs(active_runtime_config.runtime_report_interval_seconds),
);
runtime_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let mut client_change_rx = service_stats.subscribe_connection_changes();
loop {
tokio::select! {
changed = config_rx.changed() => {
match changed {
Ok(()) => {
let next_config = config_rx.borrow().clone();
apply_runtime_config_update(
&mut active_runtime_config,
next_config,
&shared_cache,
&store,
&report_context,
&mut interval,
&mut runtime_interval,
);
report_context.write_runtime_or_warn(&report_dir, "admin_config_changed", &shared_cache, &notifier, &service_stats);
}
Err(_) => {
warn!("RTR runtime config change channel closed");
}
}
continue;
}
changed = client_change_rx.changed() => {
match changed {
Ok(()) => {
report_context.write_clients_or_warn(&report_dir, "clients_changed", &shared_cache, &notifier, &service_stats);
}
Err(_) => {
warn!("RTR client connection change channel closed");
}
}
continue;
}
_ = runtime_interval.tick() => {
log_cache_memory_stats("periodic_observe", &shared_cache, &notifier);
report_context.write_runtime_or_warn(&report_dir, "runtime_periodic", &shared_cache, &notifier, &service_stats);
continue;
}
_ = interval.tick() => {}
command = source_reload_rx.recv() => {
let Some(command) = command else {
warn!("RTR source reload admin channel closed");
continue;
};
let result = perform_source_refresh(
command.phase,
command.force,
&payload_load_config,
&shared_cache,
&store,
&notifier,
&service_stats,
&report_context,
&report_dir,
&mut last_fingerprint,
);
let _ = command.respond_to.send(result.map_err(|err| err.to_string()));
continue;
}
}
let _ = perform_source_refresh(
"refresh_complete",
false,
&payload_load_config,
&shared_cache,
&store,
&notifier,
&service_stats,
&report_context,
&report_dir,
&mut last_fingerprint,
);
}
})
}
#[allow(clippy::too_many_arguments)]
fn perform_source_refresh(
phase: &'static str,
force: bool,
payload_load_config: &PayloadLoadConfig,
shared_cache: &SharedRtrCache,
store: &RtrStore,
notifier: &RtrNotifier,
service_stats: &RtrServiceStats,
report_context: &ReportContext,
report_dir: &PathBuf,
last_fingerprint: &mut Option<SourceFingerprint>,
) -> Result<SourceReloadResult> {
let source_to_delta_started = Instant::now();
let attempted_at = Utc::now();
let current_fingerprint = match latest_sources_fingerprint(payload_load_config) {
Ok(fp) => fp,
Err(err) => {
report_context.record_refresh_failure(
attempted_at,
source_to_delta_started.elapsed().as_millis(),
&err,
);
warn!(
"failed to fingerprint CCR/SLURM sources from {}: {:?} (source_to_delta_elapsed_ms={})",
payload_load_config.ccr_dir,
err,
source_to_delta_started.elapsed().as_millis()
);
report_context.write_source_or_warn(
report_dir,
"refresh_failed",
shared_cache,
notifier,
service_stats,
);
return Err(err);
}
};
report_context.record_source_fingerprint(current_fingerprint.clone());
if !force && last_fingerprint.as_ref() == Some(&current_fingerprint) {
report_context
.record_refresh_unchanged(attempted_at, source_to_delta_started.elapsed().as_millis());
info!(
"RTR source refresh skipped: source files unchanged (ccr_path={}, slurm_file_count={}, elapsed_ms={})",
current_fingerprint.ccr.path,
current_fingerprint.slurm_files.len(),
source_to_delta_started.elapsed().as_millis()
);
log_cache_memory_stats("refresh_skipped_unchanged", shared_cache, notifier);
report_context.write_source_or_warn(
report_dir,
"refresh_skipped_unchanged",
shared_cache,
notifier,
service_stats,
);
return Ok(SourceReloadResult {
phase,
changed: false,
skipped_unchanged: true,
payload_count: None,
serials: shared_cache.load_full().serials(),
});
}
let load = match load_payloads_from_latest_sources_with_report(payload_load_config) {
Ok(load) => load,
Err(err) => {
report_context.record_refresh_failure(
attempted_at,
source_to_delta_started.elapsed().as_millis(),
&err,
);
warn!(
"failed to reload CCR/SLURM payloads from {}: {:?} (source_to_delta_elapsed_ms={})",
payload_load_config.ccr_dir,
err,
source_to_delta_started.elapsed().as_millis()
);
report_context.write_source_or_warn(
report_dir,
"refresh_failed",
shared_cache,
notifier,
service_stats,
);
return Err(err);
}
};
let source = load.source;
let quality = load.quality;
let payloads = load.payloads;
let payload_count = payloads.len();
let source_snapshot = Snapshot::from_payloads(payloads);
let old_cache = shared_cache.load_full();
let old_serial = old_cache.serial_for_version(2);
let mut next_cache = old_cache.as_ref().clone();
let updated = match next_cache.update_with_snapshot(source_snapshot, store) {
Ok(()) => {
let new_serial = next_cache.serial_for_version(2);
shared_cache.store(std::sync::Arc::new(next_cache));
if new_serial != old_serial {
info!(
"RTR cache refresh applied: ccr_dir={}, payload_count={}, old_serial={}, new_serial={}",
payload_load_config.ccr_dir, payload_count, old_serial, new_serial
);
true
} else {
info!(
"RTR cache refresh found no change: ccr_dir={}, payload_count={}, serial={}",
payload_load_config.ccr_dir, payload_count, old_serial
);
false
}
}
Err(err) => {
report_context.record_refresh_failure(
attempted_at,
source_to_delta_started.elapsed().as_millis(),
&err,
);
warn!("RTR cache update failed: {:?}", err);
report_context.write_source_or_warn(
report_dir,
"refresh_failed",
shared_cache,
notifier,
service_stats,
);
return Err(err);
}
};
report_context.record_refresh_success(
attempted_at,
source_to_delta_started.elapsed().as_millis(),
updated,
source,
quality,
);
info!(
"RTR source-to-delta timing: phase={}, ccr_dir={}, payload_count={}, changed={}, elapsed_ms={}",
phase,
payload_load_config.ccr_dir,
payload_count,
updated,
source_to_delta_started.elapsed().as_millis()
);
if updated {
let listener_count = notifier.notify_cache_updated();
info!(
"RTR cache updated, notify signal emitted to session listeners: listener_count={}",
listener_count
);
}
log_cache_memory_stats(phase, shared_cache, notifier);
report_context.write_source_or_warn(report_dir, phase, shared_cache, notifier, service_stats);
last_fingerprint.replace(current_fingerprint);
Ok(SourceReloadResult {
phase,
changed: updated,
skipped_unchanged: false,
payload_count: Some(payload_count),
serials: shared_cache.load_full().serials(),
})
}
fn apply_runtime_config_update(
active: &mut RuntimeConfig,
next: RuntimeConfig,
shared_cache: &SharedRtrCache,
store: &RtrStore,
report_context: &ReportContext,
refresh_interval: &mut tokio::time::Interval,
runtime_interval: &mut tokio::time::Interval,
) {
let old = active.clone();
*active = next.clone();
report_context.update_runtime_config(&next);
{
let old_cache = shared_cache.load_full();
let mut next_cache = old_cache.as_ref().clone();
next_cache.update_runtime_config(
next.max_delta,
next.prune_delta_by_snapshot_size,
next.timing(),
store,
);
shared_cache.store(std::sync::Arc::new(next_cache));
}
if old.source_refresh_interval_seconds != next.source_refresh_interval_seconds {
*refresh_interval = tokio::time::interval(std::time::Duration::from_secs(
next.source_refresh_interval_seconds,
));
refresh_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
}
if old.runtime_report_interval_seconds != next.runtime_report_interval_seconds {
*runtime_interval = tokio::time::interval_at(
tokio::time::Instant::now()
+ std::time::Duration::from_secs(next.runtime_report_interval_seconds),
std::time::Duration::from_secs(next.runtime_report_interval_seconds),
);
runtime_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
}
info!(
"RTR runtime config applied: max_delta={}, prune_delta_by_snapshot_size={}, source_refresh_interval_seconds={}, runtime_report_interval_seconds={}, report_history_limit={}, timezone={}, timing=({}, {}, {})",
next.max_delta,
next.prune_delta_by_snapshot_size,
next.source_refresh_interval_seconds,
next.runtime_report_interval_seconds,
next.report_history_limit,
next.timezone,
next.timing.refresh,
next.timing.retry,
next.timing.expire
);
}
fn log_cache_memory_stats(phase: &str, shared_cache: &SharedRtrCache, notifier: &RtrNotifier) {
let cache = shared_cache.load_full();
let stats = cache.memory_stats();
let rss_mib = current_rss_mib();
info!(
"RTR memory observe: phase={}, listener_count={}, serials={:?}, snapshot_payload_counts={:?}, delta_lengths={:?}, delta_payload_counts={:?}, rss_mib={:?}",
phase,
notifier.listener_count(),
stats.serials,
stats.snapshot_payload_counts,
stats.delta_lengths,
stats.delta_payload_counts,
rss_mib
);
}