feat(catalog): reload manifest overlay when a refreshed catalog changes
load_manifests() only ran at startup, so a manifest published via the signed catalog sat dormant until the next service restart (found while shipping the strfry manifest fixes: package.check-updates refreshed the cache but the orchestrator kept rendering the old overlay). - refresh_catalog now reports whether the cached bytes actually changed (write_cache skips identical rewrites), so unchanged hourly polls don't churn the manifest map. - New ContainerOrchestrator::reload_manifests (default no-op); prod delegates to load_manifests — the rebuild is atomic under the state write lock, and the reconciler's durable user-stopped/uninstalled marker filters make a mid-session reload equivalent to the restart path that already runs on every boot. - package.check-updates reloads on change and reports catalog_changed + manifests_reloaded; the hourly update-scheduler tick (and its startup refresh) do the same. Tests: app_catalog 5/5 (new write_cache changed-detection test), reconcile 16/16, knows_app 1/1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2c46da387c
commit
ceb319c292
@@ -1897,6 +1897,23 @@ async fn apply_per_app_auto_updates(
|
||||
}
|
||||
}
|
||||
|
||||
/// After a catalog refresh that changed the cached bytes, rebuild the
|
||||
/// orchestrator's manifest map so registry-shipped manifest changes take
|
||||
/// effect now instead of at the next service restart.
|
||||
async fn reload_manifests_if_changed(
|
||||
refresh: crate::container::app_catalog::CatalogRefresh,
|
||||
orchestrator: &Option<std::sync::Arc<dyn crate::container::traits::ContainerOrchestrator>>,
|
||||
) {
|
||||
if !refresh.changed {
|
||||
return;
|
||||
}
|
||||
let Some(orch) = orchestrator else { return };
|
||||
match orch.reload_manifests().await {
|
||||
Ok(n) => info!("Update scheduler: catalog changed, reloaded {n} manifest(s)"),
|
||||
Err(e) => warn!("Update scheduler: manifest reload after catalog change failed: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_update_scheduler(
|
||||
data_dir: std::path::PathBuf,
|
||||
orchestrator: Option<std::sync::Arc<dyn crate::container::traits::ContainerOrchestrator>>,
|
||||
@@ -1908,11 +1925,12 @@ pub async fn run_update_scheduler(
|
||||
|
||||
// Refresh the app catalog once at startup so per-app "update available"
|
||||
// badges appear without waiting for the first hourly tick.
|
||||
if let Err(e) = crate::container::app_catalog::refresh_catalog(&data_dir).await {
|
||||
debug!(
|
||||
match crate::container::app_catalog::refresh_catalog(&data_dir).await {
|
||||
Ok(refresh) => reload_manifests_if_changed(refresh, &orchestrator).await,
|
||||
Err(e) => debug!(
|
||||
"Update scheduler: initial app-catalog refresh failed: {}",
|
||||
e
|
||||
);
|
||||
),
|
||||
}
|
||||
|
||||
loop {
|
||||
@@ -1922,8 +1940,11 @@ pub async fn run_update_scheduler(
|
||||
// populates per-app update availability (the "Update" button still has
|
||||
// to be clicked — nothing auto-applies). Best-effort; on failure the
|
||||
// previously cached catalog stays in place (origin-always-wins).
|
||||
if let Err(e) = crate::container::app_catalog::refresh_catalog(&data_dir).await {
|
||||
debug!("Update scheduler: app-catalog refresh failed: {}", e);
|
||||
// A changed catalog also reloads the orchestrator's manifest overlay so
|
||||
// catalog-shipped manifest fixes apply without a service restart.
|
||||
match crate::container::app_catalog::refresh_catalog(&data_dir).await {
|
||||
Ok(refresh) => reload_manifests_if_changed(refresh, &orchestrator).await,
|
||||
Err(e) => debug!("Update scheduler: app-catalog refresh failed: {}", e),
|
||||
}
|
||||
|
||||
// Per-app auto-update-to-latest (multi-version support). Runs every tick
|
||||
|
||||
Reference in New Issue
Block a user