Skip to main content

Rollout

Struct Rollout 

Source
pub struct Rollout {
    pub bucket_start: u8,
    pub bucket_end: u8,
}
Expand description

Half-open bucket range [bucket_start, bucket_end) over 0..100, giving this workflow a slice of the traffic on its channel.

Compared against crate::Message::routing_bucket. The engine does not derive the bucket: how a caller maps to one — a sticky hash of some request identity, a per-message random draw, round-robin — is entirely the caller’s policy and deliberately stays outside this crate.

Fields§

§bucket_start: u8

Inclusive lower bound.

§bucket_end: u8

Exclusive upper bound. 100 means “up to and including bucket 99”.

Implementations§

Source§

impl Rollout

Source

pub fn accepts(&self, bucket: u8) -> bool

Whether this range serves bucket (0..=99).

[0, 100) accepts everything. An empty or inverted range (bucket_end <= bucket_start) accepts nothing.

Source

pub fn partition(percentages: &[u8]) -> Result<Vec<Self>, RolloutError>

Turn an ordered percentage split into contiguous half-open ranges covering exactly 0..100.

Entry i gets the range starting where entry i-1 ended, so the input order is the traffic order. The percentages must sum to exactly 100: less leaves buckets that match nothing, more pushes later entries past the end of the bucket space where they can never match. The error names which.

A 0 entry is allowed and yields an empty range, which accepts nothing — the natural way to express a version that is staged but takes no traffic.

use dataflow_rs::{Rollout, RolloutError};

let split = Rollout::partition(&[90, 10]).unwrap();
assert_eq!(split[0], Rollout { bucket_start: 0, bucket_end: 90 });
assert_eq!(split[1], Rollout { bucket_start: 90, bucket_end: 100 });

// Anything the engine can route lands in exactly one range.
for bucket in 0u8..=99 {
    assert_eq!(split.iter().filter(|r| r.accepts(bucket)).count(), 1);
}

assert_eq!(Rollout::partition(&[90, 9]), Err(RolloutError::Under { total: 99 }));
assert_eq!(Rollout::partition(&[90, 11]), Err(RolloutError::Over { total: 101 }));
Source

pub fn validate_set<'a>( rollouts: impl IntoIterator<Item = &'a Self>, ) -> Result<(), RolloutError>

Check that a set of ranges — the versions of one logical workflow — partitions 0..100: every bucket served, none served twice.

Both failures are silent in production otherwise. A gap blackholes a slice of traffic; an overlap makes which version answers depend on workflow ordering rather than on the rollout.

Ranges are checked individually first, so an inverted range or one reaching past bucket 100 is reported as itself rather than as whatever downstream gap it happens to produce. Coverage is then reported at the lowest affected bucket, so the diagnosis is deterministic.

use dataflow_rs::{Rollout, RolloutError};

let good = Rollout::partition(&[50, 50]).unwrap();
assert!(Rollout::validate_set(&good).is_ok());

// Order does not matter — this is a property of the set.
let reversed: Vec<_> = good.iter().rev().copied().collect();
assert!(Rollout::validate_set(&reversed).is_ok());

let gapped = [
    Rollout { bucket_start: 0, bucket_end: 40 },
    Rollout { bucket_start: 41, bucket_end: 100 },
];
assert_eq!(
    Rollout::validate_set(&gapped),
    Err(RolloutError::Gap { bucket: 40 }),
);

Trait Implementations§

Source§

impl Clone for Rollout

Source§

fn clone(&self) -> Rollout

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Rollout

Source§

impl Debug for Rollout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Rollout

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for Rollout

Source§

impl PartialEq for Rollout

Source§

fn eq(&self, other: &Rollout) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Rollout

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Rollout

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.