feat(runtime): unregister service revisions
Add a lifecycle-serialized unregister operation that stops a resident Actor before releasing its registered and compiled Component. Return the existing not-registered and unavailable errors without leaving a partially removed live revision, and cover Actor and registration count cleanup.
This commit is contained in:
@@ -390,6 +390,55 @@ impl Runtime {
|
|||||||
self.start(&key, init_config)
|
self.start(&key, init_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stops and removes one registered service revision.
|
||||||
|
///
|
||||||
|
/// Callers must switch any external deployment reference away from this
|
||||||
|
/// revision before unregistering it.
|
||||||
|
pub fn unregister(&self, key: &ServiceKey) -> Result<(), RuntimeError> {
|
||||||
|
let _lifecycle = self
|
||||||
|
.inner
|
||||||
|
.lifecycle
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| RuntimeError::LockPoisoned)?;
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.inner
|
||||||
|
.services
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| RuntimeError::LockPoisoned)?
|
||||||
|
.contains_key(key)
|
||||||
|
{
|
||||||
|
return Err(RuntimeError::ServiceNotRegistered(key.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let actor = self
|
||||||
|
.inner
|
||||||
|
.actors
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| RuntimeError::LockPoisoned)?
|
||||||
|
.get(key)
|
||||||
|
.cloned();
|
||||||
|
if let Some(actor) = actor {
|
||||||
|
if actor.is_available() {
|
||||||
|
actor.stop()?;
|
||||||
|
} else if actor.is_alive() {
|
||||||
|
return Err(RuntimeError::ActorUnavailable(key.clone()));
|
||||||
|
}
|
||||||
|
self.inner
|
||||||
|
.actors
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| RuntimeError::LockPoisoned)?
|
||||||
|
.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner
|
||||||
|
.services
|
||||||
|
.lock()
|
||||||
|
.map_err(|_| RuntimeError::LockPoisoned)?
|
||||||
|
.remove(key);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns counts without exposing internal Engine or Store state.
|
/// Returns counts without exposing internal Engine or Store state.
|
||||||
pub fn stats(&self) -> Result<RuntimeStats, RuntimeError> {
|
pub fn stats(&self) -> Result<RuntimeStats, RuntimeError> {
|
||||||
let registered_services = self
|
let registered_services = self
|
||||||
|
|||||||
@@ -283,6 +283,23 @@ fn kv_capability_requires_a_backend_and_enforces_limits() {
|
|||||||
runtime.stop(&key).unwrap();
|
runtime.stop(&key).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unregister_stops_actor_and_releases_compiled_revision() {
|
||||||
|
let runtime = Runtime::new(RuntimeConfig::default()).expect("runtime should start");
|
||||||
|
let key = register_component(&runtime, "unregister", "echo_component.wasm", test_limits());
|
||||||
|
runtime.start(&key, Vec::new()).unwrap();
|
||||||
|
|
||||||
|
runtime.unregister(&key).unwrap();
|
||||||
|
|
||||||
|
let stats = runtime.stats().unwrap();
|
||||||
|
assert_eq!(stats.registered_services, 0);
|
||||||
|
assert_eq!(stats.running_actors, 0);
|
||||||
|
assert!(matches!(
|
||||||
|
runtime.start(&key, Vec::new()),
|
||||||
|
Err(RuntimeError::ServiceNotRegistered(missing)) if missing == key
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn registration_rejects_non_whitelisted_wasi_imports() {
|
fn registration_rejects_non_whitelisted_wasi_imports() {
|
||||||
let runtime = Runtime::new(RuntimeConfig::default()).expect("runtime should start");
|
let runtime = Runtime::new(RuntimeConfig::default()).expect("runtime should start");
|
||||||
|
|||||||
Reference in New Issue
Block a user