Skip to main content

Budget

Struct Budget 

Source
pub struct Budget {
    pub deadline: Option<Time>,
    pub poll_quota: u32,
    pub cost_quota: Option<u64>,
    pub priority: u8,
}
Expand description

A budget constraining resource usage for a task or region.

Budgets form a product semiring for combination:

  • Deadlines/quotas use min (tighter wins)
  • Priority uses max (higher urgency wins)

§Choosing a deadline constructor

Absolute vs. relative time is the classic budget footgun. Pick from this table; the at / timeout words in the names mark the semantics:

You haveYou wantUse
An absolute logical instant (Time)deadline at that instantwith_deadline
An absolute instant in raw seconds/nanosdeadline at that instantwith_deadline_at_secs / with_deadline_at_ns
now + a relative timeoutdeadline replaced by now + timeoutwith_timeout
now + a relative timeoutdeadline tightened, never loosenedtightened_by_timeout
A Cx + a relative timeoutambient budget tightened correctlyCx::budget_for_timeout

For deadline propagation across calls (HTTP → handler → DB), always use the tightening forms — replacing a deadline can silently extend it past the caller’s bound.

Fields§

§deadline: Option<Time>

Absolute deadline by which work must complete.

§poll_quota: u32

Maximum number of poll operations.

§cost_quota: Option<u64>

Abstract cost quota (for advanced scheduling).

§priority: u8

Scheduling priority (0 = lowest, 255 = highest).

Implementations§

Source§

impl Budget

Source

pub const INFINITE: Self

A budget with no constraints (infinite resources).

Source

pub const ZERO: Self

A budget with zero resources (nothing allowed).

Source

pub const MINIMAL: Self

A minimal budget for cleanup operations.

This provides a small poll quota (100 polls) for cleanup and finalizer code to run, but no deadline or cost constraints. Used when requesting cancellation to allow tasks a bounded cleanup phase.

Source

pub const fn new() -> Self

Creates a new budget with default values (priority 128, unlimited quotas).

Source

pub const fn unlimited() -> Self

Creates an unlimited budget (alias for INFINITE).

This is the identity element for the meet operation.

§Example
let budget = Budget::unlimited();
assert!(!budget.is_exhausted());
Source

pub const fn with_deadline_at_secs(secs: u64) -> Self

Creates a budget with only an absolute deadline constraint (in seconds).

The at in the name is deliberate: the value is a logical instant since the runtime epoch (“deadline AT t=30s”), not a timeout duration. For a per-operation timeout, use with_timeout or tightened_by_timeout with the current logical time.

§Example
let budget = Budget::with_deadline_at_secs(30);
assert_eq!(budget.deadline, Some(Time::from_secs(30)));
Source

pub const fn with_deadline_at_ns(nanos: u64) -> Self

Creates a budget with only an absolute deadline constraint (in nanoseconds).

The at in the name is deliberate: the value is a logical instant since the runtime epoch, not a timeout duration. For a per-operation timeout, use with_timeout or tightened_by_timeout with the current logical time.

§Example
let budget = Budget::with_deadline_at_ns(30_000_000_000); // 30 seconds
assert_eq!(budget.deadline, Some(Time::from_nanos(30_000_000_000)));
Source

pub const fn with_deadline(self, deadline: Time) -> Self

Sets the absolute logical deadline.

The deadline is an instant, not a duration. For example, Time::from_secs(30) means the runtime/lab clock value 30s, not “30 seconds from now”. For relative per-operation timeouts, use with_timeout.

Source

pub fn with_timeout(self, now: Time, timeout: Duration) -> Self

Sets the deadline to now + timeout.

Use this for request, RPC, database, and other per-operation timeout budgets. The caller supplies now explicitly so production and lab runtimes use the same deterministic time source.

§Example
let now = Time::from_secs(100);
let budget = Budget::new().with_timeout(now, Duration::from_secs(30));

assert_eq!(budget.deadline, Some(Time::from_secs(130)));
Source

pub fn tightened_by_timeout(self, now: Time, timeout: Duration) -> Self

Tightens this budget with a relative timeout, never loosening it.

Unlike with_timeout, which replaces any existing deadline, this composes via meet: the resulting deadline is min(existing_deadline, now + timeout). This is the correct operation for deadline propagation — an outer 10s request budget tightened by a 30s per-call timeout stays a 10s budget.

All other dimensions (poll quota, cost quota, priority) are preserved.

§Example
let now = Time::from_secs(100);

// Ambient deadline at t=110s; a 30s timeout must NOT loosen it.
let ambient = Budget::new().with_deadline(Time::from_secs(110));
let tightened = ambient.tightened_by_timeout(now, Duration::from_secs(30));
assert_eq!(tightened.deadline, Some(Time::from_secs(110)));

// No ambient deadline: the timeout becomes the deadline.
let open = Budget::new();
let bounded = open.tightened_by_timeout(now, Duration::from_secs(30));
assert_eq!(bounded.deadline, Some(Time::from_secs(130)));
Source

pub const fn with_poll_quota(self, quota: u32) -> Self

Sets the poll quota.

Source

pub const fn with_cost_quota(self, quota: u64) -> Self

Sets the cost quota.

Source

pub const fn with_priority(self, priority: u8) -> Self

Sets the priority.

Source

pub const fn is_exhausted(&self) -> bool

Returns true if the budget has been exhausted.

This checks only poll and cost quotas, not deadline (which requires current time).

Source

pub fn is_past_deadline(&self, now: Time) -> bool

Returns true if the deadline has passed.

Source

pub fn consume_poll(&mut self) -> Option<u32>

Decrements the poll quota by one, returning the old value.

Returns None if already at zero.

Source

pub fn combine(self, other: Self) -> Self

Combines two budgets using product semiring semantics.

  • Deadlines: min (earlier wins)
  • Quotas: min (tighter wins)
  • Priority: max (higher urgency wins)

This is also known as the “meet” operation (∧) in lattice terminology. See also: meet.

§Example
let outer = Budget::new()
    .with_deadline(Time::from_secs(30))
    .with_poll_quota(1000);

let inner = Budget::new()
    .with_deadline(Time::from_secs(10))  // tighter
    .with_poll_quota(5000);              // looser

let combined = outer.combine(inner);
assert_eq!(combined.deadline, Some(Time::from_secs(10))); // min
assert_eq!(combined.poll_quota, 1000);                    // min
Source

pub fn meet(self, other: Self) -> Self

Meet operation (∧) - alias for combine.

Computes the tightest constraints from two budgets. This is the fundamental operation for nesting budget scopes.

§Example
let parent = Budget::with_deadline_at_secs(30);
let child = Budget::with_deadline_at_secs(10);

// Child deadline is tighter, so it wins
let effective = parent.meet(child);
assert_eq!(effective.deadline, Some(Time::from_secs(10)));
Source

pub fn consume_cost(&mut self, cost: u64) -> bool

Consumes cost quota, returning true if successful.

Returns false (and does not modify quota) if there isn’t enough remaining cost quota. If no cost quota is set, always succeeds.

§Example
let mut budget = Budget::new().with_cost_quota(100);

assert!(budget.consume_cost(30));   // 70 remaining
assert!(budget.consume_cost(70));   // 0 remaining
assert!(!budget.consume_cost(1));   // fails, quota exhausted
Source

pub fn remaining_time(&self, now: Time) -> Option<Duration>

Returns the remaining time until the deadline, if any.

Returns None if there is no deadline or if the deadline has passed.

§Example
let budget = Budget::with_deadline_at_secs(30);
let now = Time::from_secs(10);

let remaining = budget.remaining_time(now);
assert_eq!(remaining, Some(Duration::from_secs(20)));
Source

pub const fn remaining_polls(&self) -> u32

Returns the remaining poll quota.

Returns the current poll quota value. A value of u32::MAX indicates effectively unlimited polls.

§Example
let budget = Budget::new().with_poll_quota(100);
assert_eq!(budget.remaining_polls(), 100);
Source

pub const fn remaining_cost(&self) -> Option<u64>

Returns the remaining cost quota, if any.

Returns None if no cost quota is set (unlimited).

§Example
let budget = Budget::new().with_cost_quota(1000);
assert_eq!(budget.remaining_cost(), Some(1000));

let unlimited = Budget::unlimited();
assert_eq!(unlimited.remaining_cost(), None);
Source

pub fn remaining(&self, now: Time) -> RemainingBudget

Returns a point-in-time snapshot of every remaining budget dimension.

This aggregates remaining_time, remaining_polls, and remaining_cost into one structure, mapping “effectively unlimited” sentinel values to None so callers can write if let Some(left) = snapshot.polls without knowing the sentinels.

The snapshot is valid at the moment of the call only; quotas continue to drain as the task runs.

§Example
let now = Time::from_secs(10);
let budget = Budget::new()
    .with_deadline(Time::from_secs(30))
    .with_poll_quota(100);

let left = budget.remaining(now);
assert_eq!(left.deadline, Some(Duration::from_secs(20)));
assert_eq!(left.polls, Some(100));
assert_eq!(left.cost, None); // no cost quota configured
Source

pub fn to_timeout(&self, now: Time) -> Option<Duration>

Converts the deadline to a timeout duration from the given time.

Returns the same value as remaining_time. This method is provided for API compatibility with timeout-based systems.

§Example
let budget = Budget::with_deadline_at_secs(30);
let now = Time::from_secs(5);

// 25 seconds remaining
let timeout = budget.to_timeout(now);
assert_eq!(timeout, Some(Duration::from_secs(25)));

Trait Implementations§

Source§

impl BudgetTimeExt for Budget

Source§

fn remaining_duration(&self, now: Time) -> Option<Duration>

Get remaining time until deadline.
Source§

fn deadline_sleep(&self) -> Option<Sleep>

Create sleep that respects budget deadline.
Source§

fn deadline_elapsed(&self, now: Time) -> bool

Check if deadline has passed.
Source§

impl Clone for Budget

Source§

fn clone(&self) -> Budget

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 Budget

Source§

impl Debug for Budget

Source§

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

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

impl Default for Budget

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Eq for Budget

Source§

impl From<Budget> for BudgetSnapshot

Source§

fn from(budget: Budget) -> Self

Converts to this type from the input type.
Source§

impl LocalToDistributed for Budget

Source§

type Distributed = BudgetSnapshot

The distributed equivalent type.
Source§

fn to_distributed(&self) -> BudgetSnapshot

Converts to the distributed equivalent.
Source§

impl PartialEq for Budget

Source§

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

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

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

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Budget

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, _span: NoopSpan) -> Self

Instruments this future with a span (no-op when disabled).
Source§

fn in_current_span(self) -> Self

Instruments this future with the current span (no-op when disabled).
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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V