succession 0.1.0

Orderly succession for single-instance helper processes: an advisory lock plus an identity record binds a helper to one run of its owner, so one orphaned by a restart can be recognized and asked to leave.
docs.rs failed to build succession-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

succession

One process per role, bound to one run of its owner — and an orderly handover when that run ends.

An advisory lock is the usual way to keep a single helper process unique: whoever takes it is the one. That works until the process that spawned the helper goes away — an update, a crash, a restart. The helper keeps running and keeps the lock, so nothing can start the helper that belongs to the new run. It does not look broken; it looks exactly like a healthy tenant.

This crate is the missing half of that lock: an identity beside it, so an onlooker can tell which run a tenant belongs to, and a helper can tell when the run it serves is over.

Usage

The owner mints one identity per run and passes it down when it spawns a helper:

use succession::{Compat, Identity};

const PROTOCOL: Compat = Compat::from_raw(18);
let me = Identity::mine(PROTOCOL);
// Answer `me` over IPC, and pass `me.run.get()` to the helper at spawn.

The helper takes the role, says who it is, and re-checks its owner on every handshake:

use succession::{Allegiance, Identity, Record, Role, Standing, Tenant};
# use succession::{Compat, Run};
# const PROTOCOL: Compat = Compat::from_raw(18);
# let spawned_by = Run::from_raw(1);
# fn owner_identity() -> Identity { Identity::mine(Compat::from_raw(18)) }
let role = Role::new("/run/my-app", "overlay");
let tenancy = role.claim()?;                       // Occupied => someone else is the overlay
tenancy.publish(&Record::new(Identity::new(spawned_by, PROTOCOL), Tenant::current()))?;

if let Standing::Superseded(because) = Allegiance::to(PROTOCOL, spawned_by).observe(owner_identity())
{
    eprintln!("stepping aside: {because}");        // dropping `tenancy` frees the role
}
# Ok::<(), Box<dyn std::error::Error>>(())

The owner's supervisor asks whether the seat is free before filling it:

use std::time::Duration;
use succession::{Role, Run, Verdict, verdict};

let role = Role::new("/run/my-app", "overlay");
match verdict(&role.occupancy()?, Run::mint(), Duration::ZERO, Duration::from_secs(15)) {
    Verdict::Start => { /* spawn the helper */ }
    Verdict::Wait => { /* look again shortly */ }
    Verdict::Evict(record) => { /* verify the pid, then ask it to leave */ }
    Verdict::EvictAnonymous => { /* a tenant that never identified itself */ }
}
# Ok::<(), std::io::Error>(())

Features

The default build decides and nothing else: no dependencies, and it never spawns, signals, exits, or resolves a path. Each feature adds one piece of glue, so a process that only needs the decision pays for nothing else.

Feature Adds Dependency
serde Serialization for the identity types, for carrying Identity on your own wire serde
sysinfo Tenant::look_up, so live process facts need not be supplied by hand sysinfo
eviction eviction::evict, which verifies a pid against the record before signalling it, escalates on a deadline, and waits for the role to be released sysinfo
supervision supervision::Supervisor, a probe/spawn/wait/back-off loop over the verdict, reporting events rather than logging them none

Whole-process-tree containment and async supervision stay out of scope; processkit does those well.

License

MIT OR Apache-2.0.