From 85e4a31f060e234532590ee2648a233b59102bab Mon Sep 17 00:00:00 2001 From: Nick Potts Date: Sat, 31 Oct 2026 16:08:40 +0000 Subject: [PATCH 1/2] Stop a replaced Corrosion after a 1 s grace instead of Docker's 10 s --- core/crates/ployzd/src/corrosion/service.rs | 18 ++++++--- .../ployzd/src/docker/managed_service.rs | 39 ++++++++++++++++++- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/core/crates/ployzd/src/corrosion/service.rs b/core/crates/ployzd/src/corrosion/service.rs index 6e0ac0cafa..557d3ec101 100644 --- a/core/crates/ployzd/src/corrosion/service.rs +++ b/core/crates/ployzd/src/corrosion/service.rs @@ -120,7 +120,7 @@ impl CorrosionConfig { let admin = AdminClient::new(self.paths.admin_socket()); let docker = Docker::connect_with_socket_defaults()?; let service = DockerService { - service: ManagedService::host(docker, self.container_name.clone(), IMAGE), + service: managed(docker, self.container_name.clone()), data_dir: self.paths.data_dir.clone(), run_dir: self.paths.run_dir.clone(), }; @@ -223,11 +223,17 @@ struct InstalledFiles { /// handle, for a reset the daemon did not finish before it stopped. pub async fn remove_retained(run_dir: &Path) -> Result<(), Error> { let docker = Docker::connect_with_socket_defaults()?; - remove( - &ManagedService::host(docker, DEFAULT_CONTAINER_NAME, IMAGE), - run_dir, - ) - .await + remove(&managed(docker, DEFAULT_CONTAINER_NAME), run_dir).await +} + +/// Seconds a replaced or removed Corrosion gets after SIGTERM. Its graceful exit waits up +/// to 5 s for gossip connections that peers keep open to go idle, and the daemon waits +/// on that stop (#3680). Nothing durable rides on it: each commit is synced before the +/// API answers, and anything a peer missed reaches it through sync, as after a crash. +const STOP_GRACE_SECONDS: i32 = 1; + +fn managed(docker: Docker, name: impl Into) -> ManagedService { + ManagedService::host(docker, name, IMAGE).with_stop_grace(STOP_GRACE_SECONDS) } async fn remove(service: &ManagedService, run_dir: &Path) -> Result<(), Error> { diff --git a/core/crates/ployzd/src/docker/managed_service.rs b/core/crates/ployzd/src/docker/managed_service.rs index ec6ed6ec6a..a93c21adba 100644 --- a/core/crates/ployzd/src/docker/managed_service.rs +++ b/core/crates/ployzd/src/docker/managed_service.rs @@ -7,7 +7,10 @@ use bollard::{ Docker, errors::Error as DockerError, models::{ContainerCreateBody, ContainerInspectResponse}, - query_parameters::{CreateContainerOptionsBuilder, RemoveContainerOptionsBuilder}, + query_parameters::{ + CreateContainerOptionsBuilder, RemoveContainerOptionsBuilder, StopContainerOptions, + StopContainerOptionsBuilder, + }, }; use ployz_core::PullPolicy; use sha2::{Digest as _, Sha256}; @@ -106,6 +109,7 @@ pub(crate) struct ManagedService { engine: Engine, name: String, image: &'static str, + stop_grace_seconds: Option, } #[derive(Clone)] @@ -120,6 +124,7 @@ impl ManagedService { engine: Engine::Host(docker), name: name.into(), image, + stop_grace_seconds: None, } } @@ -132,9 +137,17 @@ impl ManagedService { engine: Engine::Endpoint(docker), name: name.into(), image, + stop_grace_seconds: None, } } + /// Seconds Docker waits after SIGTERM before it kills the container, instead of + /// its 10 s default. + pub(crate) fn with_stop_grace(mut self, seconds: i32) -> Self { + self.stop_grace_seconds = Some(seconds); + self + } + pub(crate) async fn ensure_host( &self, desired: DesiredContainer, @@ -228,13 +241,22 @@ impl ManagedService { } async fn stop(&self) -> Result<(), DockerError> { - match self.docker().stop_container(&self.name, None).await { + match self + .docker() + .stop_container(&self.name, self.stop_options()) + .await + { Ok(()) => Ok(()), Err(error) if is_already_stopped(&error) || is_not_found(&error) => Ok(()), Err(error) => Err(error), } } + fn stop_options(&self) -> Option { + self.stop_grace_seconds + .map(|seconds| StopContainerOptionsBuilder::default().t(seconds).build()) + } + pub(crate) async fn remove(&self) -> Result<(), DockerError> { self.stop().await?; match self @@ -289,6 +311,19 @@ mod tests { assert!(!is_not_found(&response(500))); } + #[test] + fn stop_waits_docker_default_unless_given_a_grace() { + let docker = + Docker::connect_with_http("http://127.0.0.1:1", 1, bollard::API_DEFAULT_VERSION) + .unwrap(); + let service = ManagedService::host(docker, "service", "image:1"); + assert!(service.stop_options().is_none()); + assert_eq!( + service.with_stop_grace(1).stop_options().unwrap().t, + Some(1) + ); + } + fn body(image: &str) -> ContainerCreateBody { ContainerCreateBody { image: Some(image.to_owned()), From dd0ef1430d46929f9288d0fd13a14e4aab5a281e Mon Sep 17 00:00:00 2001 From: Nick Potts Date: Sat, 31 Oct 2026 16:12:07 +0000 Subject: [PATCH 2/2] Drop the stop-grace setter test and say why a killed Corrosion keeps its writes --- core/crates/ployzd/src/corrosion/service.rs | 5 +++-- core/crates/ployzd/src/docker/managed_service.rs | 13 ------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/core/crates/ployzd/src/corrosion/service.rs b/core/crates/ployzd/src/corrosion/service.rs index 557d3ec101..1f685fe94d 100644 --- a/core/crates/ployzd/src/corrosion/service.rs +++ b/core/crates/ployzd/src/corrosion/service.rs @@ -228,8 +228,9 @@ pub async fn remove_retained(run_dir: &Path) -> Result<(), Error> { /// Seconds a replaced or removed Corrosion gets after SIGTERM. Its graceful exit waits up /// to 5 s for gossip connections that peers keep open to go idle, and the daemon waits -/// on that stop (#3680). Nothing durable rides on it: each commit is synced before the -/// API answers, and anything a peer missed reaches it through sync, as after a crash. +/// on that stop (#3680). Nothing durable rides on it: a killed Corrosion keeps every +/// committed write, its graceful exit already drops queued changes, and peers resend +/// those through sync, as after a crash. const STOP_GRACE_SECONDS: i32 = 1; fn managed(docker: Docker, name: impl Into) -> ManagedService { diff --git a/core/crates/ployzd/src/docker/managed_service.rs b/core/crates/ployzd/src/docker/managed_service.rs index a93c21adba..07a39128c3 100644 --- a/core/crates/ployzd/src/docker/managed_service.rs +++ b/core/crates/ployzd/src/docker/managed_service.rs @@ -311,19 +311,6 @@ mod tests { assert!(!is_not_found(&response(500))); } - #[test] - fn stop_waits_docker_default_unless_given_a_grace() { - let docker = - Docker::connect_with_http("http://127.0.0.1:1", 1, bollard::API_DEFAULT_VERSION) - .unwrap(); - let service = ManagedService::host(docker, "service", "image:1"); - assert!(service.stop_options().is_none()); - assert_eq!( - service.with_stop_grace(1).stop_options().unwrap().t, - Some(1) - ); - } - fn body(image: &str) -> ContainerCreateBody { ContainerCreateBody { image: Some(image.to_owned()),