pub enum PlacementStrategy {
SingleNode,
Replicated,
Sharded,
}Expand description
How the Aplicacao distributes across clusters. Three options:
SingleNode— one cluster runs the app at a time; takeover on death (Erlang/OTP distributed-app semantics).Replicated— every named cluster runs an instance (active-active).Sharded— entities distribute by hash key across clusters (Akka cluster sharding).
Variants§
Implementations§
Source§impl PlacementStrategy
impl PlacementStrategy
pub const fn is_single_node(&self) -> bool
pub const fn is_replicated(&self) -> bool
pub const fn is_sharded(&self) -> bool
Source§impl PlacementStrategy
impl PlacementStrategy
Sourcepub const ALL: &'static [Self]
pub const ALL: &'static [Self]
Exhaustive iteration surface for every consumer that reads the
full closed-set (the future M4 admission-webhook’s accepted-
strategy listing in its rejection body, a future feira app placement --list CLI-side surfacing of the accepted arm-set,
any future round-trip fuzz harness). A future variant addition
(an Anycast mesh-anycast arm the MESH-COMPOSITION §II.5 hint
names as a trajectory item) extends this slice as a single edit
and every consumer picks up the new entry by construction — the
compiler-checked exhaustiveness on the sibling method match
arms is the build-time guarantee that no arm forgets to grow.
Same shape as the sibling closed-set typed enums’
RateLimitUnit::ALL (6bce03d) and
crate::dep::DepList::ALL (45ee563) exhaustive-iteration
surfaces — the third closed-set typed enum on the caixa surface
to converge onto the same discipline.
Sourcepub const fn as_str(self) -> &'static str
pub const fn as_str(self) -> &'static str
Canonical camelCase-schema discriminator scalar this variant
serializes as under crate::M3_PLACEMENT_KEY_ESTRATEGIA. The
three arms return the paired crate::M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE
/ crate::M3_PLACEMENT_ESTRATEGIA_REPLICATED /
crate::M3_PLACEMENT_ESTRATEGIA_SHARDED lifted constants so
every substrate consumer that dispatches on the strategy (the
lareira-fleet-programs aggregator, the future app-operator
reconciler, the M3 Adaptive compression pass) reads the same
byte-string the Serialize derive emits — the pin test in
[tests::placement_strategy_variants_serialize_to_lifted_scalar_values]
asserts the two paths agree.
Sourcepub fn from_wire(s: &str) -> Option<Self>
pub fn from_wire(s: &str) -> Option<Self>
Substrate-canonical reverse projection on the :placement :estrategia closed-set axis — parses the camelCase-schema
discriminator scalar back to the typed variant, or None when
s is outside the closed-set arm-string set Self::as_str
emits. Dispatches on the same lifted
crate::render::M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE /
crate::render::M3_PLACEMENT_ESTRATEGIA_REPLICATED /
crate::render::M3_PLACEMENT_ESTRATEGIA_SHARDED constants the
Self::as_str emitter walks, so the parse and emit halves of
the round-trip migrate through one caixa-core edit on any future
arm addition (an Anycast mesh-anycast arm the MESH-COMPOSITION
§II.5 hint names as a trajectory item lands one variant + one
arm per method and the compiler enforces exhaustiveness on every
consumer’s match self arms).
Prior to this lift the substrate carried only the forward
Self → &str projection (the Self::as_str emitter, the
std::fmt::Display impl routed through it, the Serialize
derive that emits the same byte-string under
crate::M3_PLACEMENT_KEY_ESTRATEGIA) — every non-serde
consumer that wanted to parse a wire-form strategy scalar had to
re-inline a three-arm match s { "SingleNode" => …, "Replicated" => …, "Sharded" => …, _ => … } cascade that expressed no
compile-time link back to the typed variant’s canonical lifted
constant. A future variant rename or a per-arm serde-attribute
drift would silently split the wire byte-string one non-serde
consumer parsed from the one the emitter wrote, with the
failure surfacing at parse time far from the rebrand commit.
Same closed-set-reverse-projection discipline the sibling
crate::CaixaKind::from_wire (2aa6d23) and
RateLimitUnit::from_suffix typed enums carry on the peer
wire-side str → Self axes — extended onto the M3 mesh-primitive-
defining :placement :estrategia closed-set axis, the third
substrate-side closed-set typed enum to converge on the two-way
str ↔ Self round-trip. Method-named from_wire (not from_str)
to match the peer crate::CaixaKind::from_wire shape verbatim
and side-step the std::str::FromStr-collision clippy
(clippy::should_implement_trait) the plain from_str name
carries; a future explicit std::str::FromStr impl can layer
on top by delegating to this canonical arm-dispatch method.
Returns Option<Self> (rather than Result<Self, _>) to match
the sibling crate::CaixaKind::from_wire shape: the caller
picks the diagnostic form appropriate for its use site — a
future feira app placement --set CLI-side arg-parse that wants
an "unknown strategy: {s} (accepted: SingleNode, Replicated, Sharded)" diagnostic builds one on top by iterating
Self::ALL, while the future M4 admission-webhook’s rejection
path folds None onto its per-CR structured refusal body.
Sourcepub const fn requires_shard_key(self) -> bool
pub const fn requires_shard_key(self) -> bool
Substrate-canonical per-arm predicate naming the cross-slot
:placement :estrategia ↔ :placement :shard-key invariant on the
closed-set typed PlacementStrategy enum: true iff the strategy
consumes the paired Placement::shard_key axis (and therefore
requires — and is the only strategy that permits — a non-empty
:shard-key on the paired slot). Today the accept-set is the
singleton {Sharded} — Sharded is the sole Akka-style
hash-keyed distribution arm (MESH-COMPOSITION §II.4) that keys off a
per-entity extractor expression; SingleNode (Erlang/OTP
distributed-app takeover — §II.1) and Replicated (active-active
across every named cluster) have no hash-keyed routing axis to
consume the slot and refuse a declared-but-inert :shard-key
through AplicacaoError::ShardKeyOnNonSharded.
Every validated Placement past AplicacaoSpec::validate_placement
satisfies placement.shard_key().is_some() == placement.estrategia().requires_shard_key() by construction — the
cross-slot partition the pin
[tests::validate_placement_admits_paired_shape_iff_strategy_requires_shard_key]
locks load-bearing, so every downstream consumer that reaches for
the paired shape (the future M4 mesh.pleme.io/v1alpha1/Aplicacao
CR materializer’s per-CR shard-key resolver, the future
[feira app graph --shard-key] per-Aplicacao column, the future
per-cluster Akka-style cluster-sharding reconciler’s per-entity
hash-routing gate, the M5 adaptive-placement engine’s per-strategy
shard-key requirement probe, a future author-facing tatara-lisp
linter that flags (:placement (:estrategia Replicated :shard-key "tenantId")) shapes before feira lint reaches
AplicacaoSpec::validate) can reach for one typed dispatch on
the substrate primitive — the predicate names the cross-slot
invariant, not the arm identity.
Prior to this lift the “does this strategy consume :shard-key”
classification lived under the gen_platform::IsVariant-derived
Self::is_sharded predicate at three fixture-builder sites in
this crate (the [tests::placement_strategy_variants_round_trip]
per-variant Placement-builder’s if s.is_sharded() { Some("$key"…) } else { None } cascade, the
[tests::estrategia_returns_placement_estrategia_verbatim_across_permutations]
per-variant Placement-builder’s estrategia.is_sharded().then(|| "tenantId".to_string()) cascade, and the
[tests::validate_placement_reads_through_lifted_estrategia_accessor]
per-variant spec-mutator’s identical .is_sharded().then(…)
cascade). Each site conflated two semantically distinct questions:
“is the variant Sharded?” (arm-identity, what
Self::is_sharded answers) and “does the variant consume
:shard-key?” (cross-slot-invariant, what this predicate answers).
The two questions land on the same three-way answer under today’s
closed accept-set (both trip on the singleton {Sharded}), but a
future arm addition that consumed :shard-key under a different
name (a hypothetical Anycast mesh-anycast arm the MESH-COMPOSITION
§II.5 roadmap-hint names that hash-partitions across the cluster
pool by client-IP hash rather than an author-declared extractor
expression, a hypothetical WeightedShard variant that carries a
shard-key + per-cluster weight table under a promoted M5
adaptive-placement engine) or an addition that did not consume
:shard-key on a semantically Sharded-shaped arm would silently
split the two questions. Any consumer that read
.is_sharded().then(…) for the shard-key requirement gate would
silently misclassify the new arm as non-consuming — a fixture
builder would omit :shard-key where the new arm required one and
AplicacaoSpec::validate_placement would refuse the fixture with
AplicacaoError::ShardedWithoutKey far from the arm-addition
commit, a future M4 CR materializer would fall through the
.is_sharded()-only branch to the non-shard-key resolver arm and
silently emit an empty extractor at the Akka reconciler layer.
Lifting the classification as a substrate-primitive method on the
closed-set typed enum names the cross-slot invariant on the
primitive that owns the partition: every future arm addition
declares its :shard-key consumption in one place (this predicate’s
match self arm-set), and every downstream consumer that reaches
for the paired shape reads through one typed dispatch. Same
discipline as the sibling WitContract::is_capability (7b97d26)
per-arm predicate on the pre-projection WIT-shape axis and the
WitTarget::is_capability gen_platform::IsVariant-derived
paired predicate on the post-projection typed-view axis — a
per-arm semantic-classification predicate paired with the
arm-identity predicate the derive already emits, closing the drift
footgun on the cross-slot invariant axis.
Method-named requires_shard_key (not has_shard_key, not
is_shard_keyed, not takes_shard_key) because the cross-slot
invariant reads as “this strategy requires the paired
:shard-key axis” — the SingleNode/Replicated arms refuse
the axis through AplicacaoError::ShardKeyOnNonSharded, not
merely omit it. The has_* framing would read as an accessor
(returning the presence of an already-carried value) rather than a
requirement (naming the invariant the paired slot must satisfy).
Returns bool (not Option<()> or a marker-type witness), same
shape as the sibling WitContract::is_capability /
Self::is_sharded per-arm boolean predicates on the closed-set
arm-family, so every consumer reaches for .requires_shard_key()
as a drop-in replacement for the .is_sharded() conflated read
without a return-shape migration.
Trait Implementations§
Source§impl Clone for PlacementStrategy
impl Clone for PlacementStrategy
Source§fn clone(&self) -> PlacementStrategy
fn clone(&self) -> PlacementStrategy
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for PlacementStrategy
Source§impl Debug for PlacementStrategy
impl Debug for PlacementStrategy
Source§impl Default for PlacementStrategy
impl Default for PlacementStrategy
Source§impl<'de> Deserialize<'de> for PlacementStrategy
impl<'de> Deserialize<'de> for PlacementStrategy
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>,
Source§impl Display for PlacementStrategy
std::fmt::Display routed through PlacementStrategy::as_str, so
the pretty-printed byte-string every consumer that formats the strategy
as user-facing text lands on (the M3 AplicacaoError::PlacementWithoutClusters
/ AplicacaoError::ShardKeyOnNonSharded #[error(":placement {estrategia} …")] diagnostic templates, the future feira app graph
per-Aplicacao strategy line, the future M4 CR materializer’s per-
admission-webhook rejection body) reaches for the same lifted
crate::M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE /
crate::M3_PLACEMENT_ESTRATEGIA_REPLICATED /
crate::M3_PLACEMENT_ESTRATEGIA_SHARDED const the wire-format
Serialize derive already emits under
crate::M3_PLACEMENT_KEY_ESTRATEGIA and the
PlacementStrategy::as_str helper already returns.
impl Display for PlacementStrategy
std::fmt::Display routed through PlacementStrategy::as_str, so
the pretty-printed byte-string every consumer that formats the strategy
as user-facing text lands on (the M3 AplicacaoError::PlacementWithoutClusters
/ AplicacaoError::ShardKeyOnNonSharded #[error(":placement {estrategia} …")] diagnostic templates, the future feira app graph
per-Aplicacao strategy line, the future M4 CR materializer’s per-
admission-webhook rejection body) reaches for the same lifted
crate::M3_PLACEMENT_ESTRATEGIA_SINGLE_NODE /
crate::M3_PLACEMENT_ESTRATEGIA_REPLICATED /
crate::M3_PLACEMENT_ESTRATEGIA_SHARDED const the wire-format
Serialize derive already emits under
crate::M3_PLACEMENT_KEY_ESTRATEGIA and the
PlacementStrategy::as_str helper already returns.
Until this lift landed the sibling OTP-shape typed enums —
crate::supervisor::RestartStrategy / crate::supervisor::RestartPolicy
(both derive gen_platform::Discriminant with #[discriminant(also_display)]
so std::fmt::Display routes through the same discriminant string
the wire format emits) — carried a stable std::fmt::Display
surface but PlacementStrategy did not; every consumer reaching
for a strategy byte-string past the wire format had to pick between
three paths (PlacementStrategy::as_str, the Serialize derive’s
serialized string, format!("{variant:?}") on the std::fmt::Debug
derive), any two of which a future variant rename or
#[serde(rename_all = "kebab-case")] attribute would silently
desynchronize — with the failure surfacing as a downstream renderer /
operator’s per-strategy dispatch reading one spelling while the wire
format emitted another, far from the source rebrand commit and with
no field naming the drift. Routing Display through
PlacementStrategy::as_str makes the three paths
(Debug for structural inspection, Display for user-facing text,
Serialize for the wire format) converge on the same lifted
[crate::M3_PLACEMENT_ESTRATEGIA_*] const set: the wire byte-string,
the diagnostic byte-string, and the pretty-printed byte-string move
as a single unit through one canonical declaration each, by
construction. Same trajectory as PlacementStrategy::as_str
(cc8f749) on the sibling wire-vs-const single-source axis — this lift
closes the third path.
Pin tests
[tests::placement_strategy_display_routes_through_as_str_helper]
and
[tests::placement_strategy_display_matches_serialized_wire_byte_string]
assert the three paths agree byte-for-byte on every variant, so a
future variant rename or per-arm serde attribute drift is a build
error visible at caixa-core test time, not a silent per-consumer
dispatch miss at apply / reconcile time.
impl Eq for PlacementStrategy
Source§impl Hash for PlacementStrategy
impl Hash for PlacementStrategy
Source§impl PartialEq for PlacementStrategy
impl PartialEq for PlacementStrategy
Source§impl Serialize for PlacementStrategy
impl Serialize for PlacementStrategy
impl StructuralPartialEq for PlacementStrategy
Auto Trait Implementations§
impl Freeze for PlacementStrategy
impl RefUnwindSafe for PlacementStrategy
impl Send for PlacementStrategy
impl Sync for PlacementStrategy
impl Unpin for PlacementStrategy
impl UnsafeUnpin for PlacementStrategy
impl UnwindSafe for PlacementStrategy
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.