pub enum UpgradeInstruction {
LoadModule {
module: String,
},
StateChange {
script: PathBuf,
},
SoftPurge {
module: String,
},
Purge {
module: String,
},
Restart,
}Expand description
One upgrade instruction. The set mirrors OTP’s appup low-level instructions: enough to express every common upgrade pattern, few enough that the wasm-operator can implement each deterministically.
Variants§
LoadModule
Load a new wasm module alongside the current one — the analog
of OTP’s code:load_module/1. Both versions remain in memory
after this instruction; in-flight requests stay on the old
version, new requests route to the new version.
StateChange
Run a state-migration tatara-lisp file. Receives the old state
- the prior version string; returns the new state. Analog of
gen_server:code_change/3.
SoftPurge
Wait for in-flight requests on a named module to drain, then
GC it — the analog of code:soft_purge/1. Default cooldown is
60s; longer-running requests block the upgrade.
Purge
Discard a named module immediately, without waiting for
drain — the analog of code:purge/1. Used when we don’t
care about in-flight callers (cron, oneShot).
Restart
Fall back to a full restart for this entry. Used when a typed upgrade is impossible (e.g. wasm component world incompatible).
Implementations§
Source§impl UpgradeInstruction
impl UpgradeInstruction
Sourcepub const fn discriminant(&self) -> &'static str
pub const fn discriminant(&self) -> &'static str
Stable variant discriminant — auto-generated by
#[derive(Discriminant)]. The string IS the wire
identifier for metrics labels / audit-log tags /
rate-limit keys; renaming an existing variant is a
breaking change.
Source§impl UpgradeInstruction
impl UpgradeInstruction
pub const fn is_load_module(&self) -> bool
pub const fn is_state_change(&self) -> bool
pub const fn is_soft_purge(&self) -> bool
pub const fn is_purge(&self) -> bool
pub const fn is_restart(&self) -> bool
Source§impl UpgradeInstruction
impl UpgradeInstruction
Sourcepub fn validate(&self) -> Result<(), UpgradeError>
pub fn validate(&self) -> Result<(), UpgradeError>
Validate the instruction’s typed shape. Path existence is
checked separately by crate::layout::StandardLayout.
The per-variant scalar the value-shape gates fire against is
read through this method’s two sibling accessors — the
String-carrying axis via Self::declared_module (the
LoadModule / SoftPurge / Purge variants unifying on their
K8s DNS-1123-label :module reference) and the PathBuf-
carrying axis via Self::declared_path (the StateChange
variant’s tatara-lisp :script) — rather than the per-arm
Self::LoadModule { module } | Self::SoftPurge { module } | Self::Purge { module } pattern the module-axis previously
open-coded and the per-arm Self::StateChange { script } the
script-axis previously open-coded. Every scalar this enum
carries now flows through one of the two Option<&…>
accessors, so a future extension of either axis (a fifth
module-bearing variant, an operator-side pre-parsed scalar
cache the accessors materialize behind the same return
contract, an M4 typed sub-slot the accessors could route
alongside the existing scalar) migrates as a single edit on
the accessor rather than a coordinated rewrite of every
downstream value-shape gate. Restart (the only variant that
carries neither scalar) falls through both Option checks and
returns Ok(()) — the terminal-fallback shape the
Self::Restart variant doc pins.
Sourcepub fn declared_module(&self) -> Option<&str>
pub fn declared_module(&self) -> Option<&str>
The :module scalar carried by this instruction — the
K8s DNS-1123-label OTP-appup caixa-name reference every
Self::LoadModule / Self::SoftPurge / Self::Purge
variant declares against, and every author expects feira lint
to name verbatim in per-instruction diagnostics. Returns None
on Self::StateChange (which carries a :script — closed by
the sibling Self::declared_path) and on Self::Restart
(which carries no data at all, the OTP terminal-fallback
shape).
Sibling in shape to Self::declared_path on the second and
final scalar-carrying axis of UpgradeInstruction:
declared_path closes the PathBuf-carrying arm
(StateChange); declared_module closes the String-carrying
arms (LoadModule / SoftPurge / Purge). Every scalar the
enum carries now routes through one of the two Option<&…>
accessors — a caller that doesn’t care which variant declared
the scalar reads through one if let Some(…) rather than a
per-variant pattern match. The pair is the enum-variant-
unifying peer of the per-mesh-slot-atom scalar-accessor family
on the M3 side (crate::WitContract::source /
crate::WitContract::destination /
crate::WitContract::world_ref closing :contratos;
crate::Entrada::hostname / crate::Entrada::destination
closing :entrada; crate::Membro::nome /
crate::Membro::versao_requirement closing :membros) and
on the M2 side (crate::UpgradeFromEntry::prior_versao
closing per-entry :from; the crate::LimitsSpec /
crate::BehaviorSpec closed families; the crate::ChildSpec
closed OTP-shape supervisor family) — those peer accessors
return a struct field verbatim; this pair unifies enum-
variant-carried scalars into one accessor per typed axis.
Byte-for-byte from the typed variant’s own String storage;
no cloning, no re-parsing. A future extension of the axis (an
M4 typed sub-slot the module string is derived from, an
operator-side pre-parsed caixa-name cache the accessor could
materialize behind the same &str return contract, a fifth
module-bearing OTP-appup variant the enum grows) migrates as
a single caixa-core edit rather than a coordinated rewrite
of every downstream module-axis consumer (currently
Self::validate’s DNS-1123-label gate through
[validate_module]; extensible to future consumers on the
same axis without further per-variant match sites).
Sourcepub fn declared_path(&self) -> Option<&PathBuf>
pub fn declared_path(&self) -> Option<&PathBuf>
If the instruction references an on-disk path, return it — used by the layout checker to verify the path resolves.
Sibling on the PathBuf-carrying axis to Self::declared_module
on the String-carrying axis: declared_path closes the
StateChange arm’s :script; declared_module closes the
LoadModule / SoftPurge / Purge arms’ :module. Together
they route every scalar this enum carries through one of two
Option<&…> accessors, so Self::validate’s value-shape
gates dispatch on the accessor return rather than a per-variant
pattern match on the enum shape itself.
Four per-UpgradeInstruction consumers now key off this
accessor’s PathBuf-carrying axis:
Self::validate’s per-StateChange sandbox-path fan-out,
[crate::layout::StandardLayout::verify]’s per-StateChange
script-existence fan-out at caixa-core/src/layout.rs:1058, the
within-entry
UpgradeFromEntry::validate_state_change_singularity (2bf3ce5)
per-StateChange script-projection fan-out, and the cross-slot
validate_upgrade_from_against_behavior :upgrade-from ↔ :behavior composition gate’s per-StateChange detection loop
— every downstream consumer of the PathBuf-carrying axis
reaches through this one dispatch, so a future accessor
extension (an M4 typed sub-slot the script path is derived from,
an operator-side pre-resolved-path cache the accessor
materializes behind the same Option<&PathBuf> return contract,
a fifth PathBuf-bearing OTP-appup variant the enum grows)
migrates as a single caixa-core edit rather than a coordinated
rewrite of four call sites.
Sourcepub const fn is_cleanup(&self) -> bool
pub const fn is_cleanup(&self) -> bool
Substrate-canonical per-UpgradeInstruction OTP-appup cleanup-
family arm-discriminator predicate every within-entry cross-
instruction cleanup-facing gate keys off — true iff self is
Self::SoftPurge (code:soft_purge/1 analog: drain the
named module until no process is running it, then GC) or
Self::Purge (code:purge/1 analog: discard the named
module immediately, without waiting for drain), the two OTP
two-phase-code-load cleanup arms the closed-set enum’s
non-terminal / non-migration / non-load variants exhaust.
Every non-cleanup arm (Self::LoadModule on the paired
two-phase-load half, Self::StateChange on the
gen_server:code_change/3-analog migration axis,
Self::Restart on the OTP terminal-fallback shape)
returns false.
Prior to this lift the Self::SoftPurge { module } | Self::Purge { module } two-arm cleanup-family pattern-
match sat inline at three within-entry cross-instruction
gate sites, each hand-rolling its own copy of the union
with no compile-time link back to the substrate primitive’s
closed-set arm-family: UpgradeFromEntry::validate_purge_ordering
at caixa-core/src/upgrade.rs:570 (guarded arm firing
UpgradeError::PurgeWithoutPriorLoad on any cleanup
arriving before a preceding Self::LoadModule),
UpgradeFromEntry::validate_state_change_before_cleanup
at caixa-core/src/upgrade.rs:689 (sticky-once latch
recording the first-encountered cleanup so a subsequent
Self::StateChange fires UpgradeError::StateChangeAfterCleanup),
and UpgradeFromEntry::validate_cleanup_singularity at
caixa-core/src/upgrade.rs:800 (per-module cleanup-target
dedup ejecting UpgradeError::DuplicateCleanup on the
second cleanup targeting the same :module). Three open-
coded per-arm-union pattern-matches that expressed no
compile-time link back to the substrate primitive. A future
fifth cleanup-shaped variant (a Discard variant the
code:delete/1 peer inspires that folds under the same
two-phase-load cleanup partition, an M4 SoftPurge split
into SoftPurgeCoop / SoftPurgeForce peers as the drain-
cool-down policy grows a two-arm shape, an operator-side
pre-resolved cleanup-decision cache the predicate could
route through the same bool return contract) would have
had to be threaded through every open-coded per-arm-union
pattern-match in lockstep or one gate would silently
classify the new arm outside the cleanup family while the
peer gates classified it in (or vice versa) — a
classification split across the three within-entry cross-
instruction gates at build time that lands far from the
source UpgradeInstruction declaration with no field
naming which gate carries the drifted arm-set. Lifting the
resolution to a typed predicate on the substrate primitive
means every downstream cleanup-facing consumer of the
UpgradeInstruction closed-set enum reaches for exactly
one typed dispatch — the resolver’s arm-set migrates as a
unit on any future arm addition composing under this
predicate’s || chain.
Sibling in shape to the peer gen_platform::IsVariant-
derive-generated Self::is_restart terminal-fallback
arm-discriminator predicate on the same closed-set
UpgradeInstruction enum (each names an OTP-appup arm-
family partition as one typed dispatch on the substrate
primitive; is_restart on the single-arm terminal-
fallback family, is_cleanup on the two-arm cleanup
family), extended here from the single-arm case onto the
two-arm arm-family union case. Composes through the
gen_platform::IsVariant-derive-generated
Self::is_soft_purge / Self::is_purge per-variant
predicates rather than an open-coded raw matches!
pattern-match, so a future rebrand on either underlying
per-arm classifier flows through this predicate’s one
body without a coordinated per-consumer rewrite across
the three within-entry cross-instruction gates that route
through it. Peer of the sibling per-:contratos
shape-family union predicates crate::WitContract::is_http /
crate::WitContract::is_pubsub / crate::WitContract::is_store
on the M3 mesh-slot per-:wit world-ref axis (each unions a
per-shape WIT-prefix rule the substrate primitive’s arm-
family partition names as one typed dispatch) — the same
“one typed dispatch on the substrate primitive, thin
projections at each consumer” discipline extended onto the
M2 :upgrade-from :instructions per-UpgradeInstruction
cleanup-family axis.
The name is_cleanup maps directly onto the canonical
OTP-appup vocabulary (INSPIRATIONS §II.4 verbatim: “2.
code:soft_purge/1 — wait until no process is running v1,
then discard. (code:purge/1 kills v1 immediately if you
don’t care.)” — the two code:*_purge/1 operations are
the two-phase-load contract’s cleanup half, paired under
one concept), and the peer [Self::validate_cleanup_singularity]
/ UpgradeError::DuplicateCleanup / UpgradeError::PurgeWithoutPriorLoad
/ UpgradeError::StateChangeAfterCleanup surface already
reaches for the same “cleanup” vocabulary in identifier +
diagnostic form.
Trait Implementations§
Source§impl Clone for UpgradeInstruction
impl Clone for UpgradeInstruction
Source§fn clone(&self) -> UpgradeInstruction
fn clone(&self) -> UpgradeInstruction
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for UpgradeInstruction
impl Debug for UpgradeInstruction
Source§impl<'de> Deserialize<'de> for UpgradeInstruction
impl<'de> Deserialize<'de> for UpgradeInstruction
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for UpgradeInstruction
Source§impl PartialEq for UpgradeInstruction
impl PartialEq for UpgradeInstruction
Source§impl Serialize for UpgradeInstruction
impl Serialize for UpgradeInstruction
impl StructuralPartialEq for UpgradeInstruction
Source§impl TypedDispatcher for UpgradeInstruction
impl TypedDispatcher for UpgradeInstruction
Source§fn variant_kinds() -> Vec<&'static str>
fn variant_kinds() -> Vec<&'static str>
Source§fn variant_fields() -> Vec<(&'static str, Vec<&'static str>)>
fn variant_fields() -> Vec<(&'static str, Vec<&'static str>)>
inherit (variant) <fields> Nix patterns.Source§fn variant_count() -> usize
fn variant_count() -> usize
Auto Trait Implementations§
impl Freeze for UpgradeInstruction
impl RefUnwindSafe for UpgradeInstruction
impl Send for UpgradeInstruction
impl Sync for UpgradeInstruction
impl Unpin for UpgradeInstruction
impl UnsafeUnpin for UpgradeInstruction
impl UnwindSafe for UpgradeInstruction
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.