Skip to main content

Schedule

Struct Schedule 

Source
pub struct Schedule {
    pub id: String,
    pub when: When,
    pub job_id: String,
    pub plan: FanoutPlan,
    pub active: Active,
    pub constraints: Constraints,
    pub on_failure: OnFailure,
    pub tz: ScheduleTz,
    pub starting_deadline: Option<String>,
    pub runs_on: RunsOn,
    pub enabled: bool,
}
Expand description

Periodic schedule (spec §2.4.3). v0.18.0 carries the fanout plan (target + optional rollout + optional jitter) inline; the referenced job (job_id → [BUCKET_JOBS]) supplies only the script body. Two schedules of the same job can target different groups on different cadences without copying the manifest.

#418 Phase 1: the cadence is the single When field. The old cron × mode × cooldown × auto_disable_when_done quartet is gone (no back-compat — pre-Phase-1 KV blobs fail to parse and are warn-skipped; re-schedule create to upgrade them). The engine underneath is unchanged: Schedule::lowered maps when onto the same (cron, ExecMode, cooldown) trio the scheduler and decide_fire always ran on.

Fields§

§id: String§when: When

When to fire — a reconcile cadence (per_pc / per_target) or a calendar time trigger (at / days). See When.

singleton_map: serde_yaml 0.9 renders externally-tagged enums as !per_pc YAML tags by default; this keeps the operator-facing map shape (when: { per_pc: once }). JSON output is identical either way, and the schemars schema (external tagging = oneOf of single-key objects) already matches the singleton-map wire shape.

§job_id: String

Key into crate::kv::BUCKET_JOBS. Must equal a registered Manifest’s id.

§plan: FanoutPlan

Who + how-to-phase + when-to-stagger. The Manifest doesn’t carry these any more — same job + different fanout = different schedule.

§active: Active

Optional validity window. Outside [from, until) the schedule is dormant — still registered, still visible, but every tick is skipped (deleted ≠ dormant: a campaign that ended stays inspectable and can be re-armed by editing the window). Checked at tick time on both the backend scheduler and the agent’s local scheduler.

§constraints: Constraints

#418 Phase 3: operational constraints gating when within an active period a fire may happen. Currently just window (a maintenance time-of-day window); future require (env gates) and max_concurrent land in the same namespace. Evaluated in the schedule’s tz like the other wall-clock fields. Checked at tick time on both schedulers.

§on_failure: OnFailure

#418 Phase 4: what to do after a fire’s script comes back failed. Currently just retry (fixed-backoff in-process re-run); future notify / disable join the same namespace. Applied fire-side in handle_command (the retry policy is lowered onto every Command this schedule produces), so it covers both runs_on locations.

§tz: ScheduleTz

#418 Phase 2: the timezone this schedule’s wall-clock fields are evaluated in — both the calendar at firing time AND the active.{from,until} window bounds. local (default) = the running host’s TZ (the agent’s for runs_on: agent, the backend server’s otherwise); utc for TZ-independent schedules. Reconcile shapes (per_pc/per_target) ignore it for firing (poll cron runs every minute regardless) but still honor it for the active window.

§starting_deadline: Option<String>

v0.22: optional humantime window after a cron tick during which the Command is still considered “live”. The scheduler computes tick_at + starting_deadline and stamps it onto each Command as deadline_at; agents skip Commands they receive after that absolute time. None (default) = no deadline, meaning a Command queued in the broker / stream during agent downtime runs whenever the agent reconnects — good for kitting / inventory / cleanup. Set this for time-of-day notifications, lunch reminders, etc., where “fire 3 hours late” would be wrong.

§runs_on: RunsOn

v0.23: where does the cron tick happen? Backend (default, historical) = backend’s scheduler fires Commands via NATS; agents passively receive. Agent = each targeted agent runs its own internal cron and fires locally, so the schedule keeps ticking even when the broker is unreachable (laptop on the train, broker maintenance window, full WAN outage). The two locations are mutually exclusive — when Agent, the backend scheduler stays out and just keeps the definition in KV for agents to read.

§enabled: bool

Implementations§

Source§

impl Schedule

Source

pub fn bad_window(&self) -> Option<String>

The error message if this schedule’s constraints.window is set but unparseable, else None. The scheduler logs this at register time so a fail-closed (never-firing) schedule from a hand-edited KV blob is diagnosable (gemini #452 review).

Source

pub fn calendar_outside_window(&self) -> bool

True when this is a calendar schedule whose fire time can never fall inside its constraints.window — the cron fires, the window check rejects it, and (firing only at that time-of-day) it effectively never runs. An easy misconfig to set up by accident; the scheduler warns at register time (claude #452 review). Reconcile shapes poll every minute, so they always catch the window opening and aren’t affected.

Source

pub fn preview_fires( &self, now: DateTime<Utc>, count: usize, ) -> Vec<DateTime<Utc>>

Up to count future instants this schedule will fire, as absolute UTC, strictly after now — the dry-run / preview surface (#418 “ドライラン / プレビュー”). Only calendar schedules have discrete fire times; reconcile shapes (per_pc/per_target) poll every minute gated by cooldown, so they return an empty vec and the caller describes the cadence instead. Occurrences outside the active.{from,until} window or the constraints.window are skipped, so the list reflects when the schedule will ACTUALLY run, not the raw cron ticks. Evaluated in the schedule’s tz, exactly like the scheduler’s Job::new_async_tz, and with the same croner config the scheduler / Schedule::validate use, so a preview can never disagree with a real fire. A schedule that can never fire (a calendar time wholly outside its window, a past one-shot, enabled: false is not considered here — callers gate on enabled separately) yields an empty vec.

Source

pub fn lowered(&self) -> Lowered

Lower the operator-facing when onto the engine vocabulary. Single seam shared by the backend scheduler and the agent’s local scheduler so the two can never drift.

Source

pub fn next_calendar_fire(&self, now: DateTime<Utc>) -> Option<DateTime<Utc>>

The next absolute (UTC) time this schedule fires, or None when it has no discrete upcoming fire to preview.

Used by the KLP maintenance.list preview (“what’s about to happen on my PC”, SPEC §2.1). Returns None for:

  • reconcile shapes (per_pc / per_target) — they lower to the every-minute POLL_CRON and re-converge state continuously, so “next fire” is always ~60s away and means nothing to a user previewing upcoming maintenance;
  • a calendar schedule whose lowered cron won’t parse (a hand-edited KV blob that slipped past Schedule::validate);
  • a cron with no future occurrence.

The wall-clock fire is evaluated in the schedule’s own tz (matching the live tick’s Job::new_async_tz) then normalised to UTC for the wire. inclusive = false: strictly the next fire after now, never one matching the current instant.

Source

pub fn validate(&self) -> Result<(), String>

Cross-field semantic checks that don’t fit pure serde derive — the Manifest::validate counterpart (#418 decision F; pre-Phase-1 a broken schedule was accepted at create time and silently warn-skipped at tick time). Run at every create site: kanade schedule create (client-side) and POST /api/schedules. The job_id-exists check lives in the API handler instead — it needs the JOBS KV.

Trait Implementations§

Source§

impl Clone for Schedule

Source§

fn clone(&self) -> Schedule

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 Debug for Schedule

Source§

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

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

impl<'de> Deserialize<'de> for Schedule

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 JsonSchema for Schedule

Source§

fn schema_name() -> Cow<'static, str>

The name of the generated JSON Schema. Read more
Source§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

fn json_schema(generator: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
Source§

fn inline_schema() -> bool

Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being re-used where possible using the $ref keyword. Read more
Source§

impl Serialize for Schedule

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

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<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

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

Source§

fn __clone_box(&self, _: Private) -> *mut ()

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: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
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

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more