Skip to main content

Queue

Struct Queue 

Source
pub struct Queue { /* private fields */ }
Expand description

A queue on disk.

Implementations§

Source§

impl Queue

Source

pub fn open() -> Self

The operator’s queue, <home>/queue.

Source

pub fn at(root: PathBuf) -> Self

A queue at an explicit root. Tests use this; so could an operator who wants a queue per project.

Source

pub fn root(&self) -> &Path

Directory holding the task files.

Source

pub fn path_of(&self, id: &str) -> PathBuf

Path for one task id.

Source

pub fn attachments_dir(&self, id: &str) -> PathBuf

Directory holding a task’s attachments. A sibling of the task file, not a *.json, so Queue::list and Queue::revision never see it.

Source

pub fn attach( &self, task: &mut Task, sources: &[PathBuf], ) -> Result<Vec<String>>

Copy each of sources into task’s attachment directory and record the stored names on task (persist with Queue::put). Returns the names, in order.

Every source is checked (a readable file, a name passing crate::ask::valid_asset_name) before anything is copied, and a copy that fails part-way removes only what this call created. A name already taken is never overwritten: it becomes stem-2.ext, stem-3.ext, … A name that does not validate is refused rather than sanitised - the operator renames the file.

Source

pub fn attach_and_put( &self, task: &mut Task, sources: &[PathBuf], ) -> Result<Vec<String>>

Queue::attach then Queue::put, undoing the copies when the record cannot be written. Without the undo the files sit in <id>.attachments/ with no task pointing at them.

Only what this call copied is removed (the names attach returned, and the directory itself only when it did not exist before and is empty), never an attachment an earlier save recorded. task is rolled back too, and the error the caller sees is put’s own. This relies on a failed put leaving the stored record untouched, which its write-then-rename guarantees.

Source

pub fn attachment_paths(&self, task: &Task) -> Vec<PathBuf>

Absolute paths of task’s attachments, whatever shape this queue’s root has. Absolute because the prompt hands them to an agent whose working directory is somewhere else entirely.

Source

pub fn put(&self, task: &mut Task) -> Result<()>

Write a task, atomically, so a daemon killed mid-write leaves the previous state readable rather than a truncated file.

Runs already recorded on disk that task does not carry are kept, not dropped: magi run --task links a run into a task from another process (Queue::link_run) while a daemon may hold a snapshot taken before it, and writing that snapshot back whole would erase the link. Nothing ever removes a run from a task, so the union loses nothing.

Source

pub fn create_new(&self, task: &mut Task) -> Result<bool>

Write task only if no task with its id exists yet; Ok(false) when one does. Unlike Queue::put it never replaces a record, so a producer with a deterministic id cannot rewind a task that has since started running or finished.

Append run to the runs of the task id (or an unambiguous prefix), leaving everything else about it alone — see Task::link_run. Read and written back in one breath, because Queue::put replaces the whole record. Returns the task as stored.

Source

pub fn get(&self, id: &str) -> Result<Task>

Load a task by id or unambiguous id prefix.

Source

pub fn remove( &self, id: &str, in_flight: bool, questions: &Questions, ) -> Result<Removal>

Remove a task, and the claim lock that belongs to it.

in_flight comes from the caller — a live daemon’s heartbeat naming this task — because the task’s own running status cannot answer the question. A daemon killed mid-competition leaves the status at running and an orphaned .lock behind, and a guard that trusted either would make the task undeletable for good: the phone showed exactly that, refusing a task whose daemon had been gone for an hour.

So the lock is removed with the task rather than respected. Any lock still there once no live daemon claims the task is by definition stale, and leaving it would make a deleted task look claimed to Queue::claim and to whoever reads the directory.

A deletion is deliberate, so whatever was blocked on the id is resolved, not held: a <id>.removed tombstone is written first (it is what tells missing_blockers and the daemon’s resolver that this id was deleted on purpose, so a pass that races this call cannot mistake it for a vanished dependency), the record is deleted (the tombstone is taken back if that fails), and then each dependent is rewritten under its claim - see Queue::release_dependents_of. Best-effort for the dependents: one claimed by something else right now, or whose write fails, is released silently by crate::daemon::resolve_blockers (or crate::triage::run_once) on its next pass, because the tombstone is still there. It does not fail this removal.

questions is the store missing_blockers checks a blocked_by id against - the same store the caller already resolves id’s own home from, passed in rather than reopened here so a test queue at an explicit root never reads the operator’s real questions directory.

Source

pub fn apply_deleted_blockers(&self, task: &mut Task) -> Vec<String>

Apply Task::dependency_deleted to task for every blocked_by id that Queue::remove deleted. Returns those ids. The caller holds the task’s claim and saves it.

Source

pub fn note_dependency_deleted(&self, dependent: &Task, deleted: &str)

Record, as a fixed-wording info notice, that dependent stopped waiting on the deleted task deleted. Best-effort and beside the queue, like the hold notice Queue::put files.

Source

pub fn list(&self) -> Vec<Task>

Every task on disk, highest priority first and newest first within a priority. This is what magi task list and GET /api/queue print, so a raised priority has to move a task here the moment it is saved, not only in Queue::next_runnable’s own ordering - the operator reading the backlog and the loop about to drain it must agree on what “first” means. Every existing task defaults to priority 0, so this is a no-op change from the old newest-first order for a queue nobody has reprioritised.

Unreadable files are skipped rather than fatal: one corrupt task must not take the queue - or the web UI, or an unattended daemon - down with it.

Source

pub fn superseded(&self) -> HashMap<String, String>

Runs that a later attempt at the same task replaced, mapped to the id of the attempt that replaced them.

A task keeps its attempts in order, and the deck showed them as two cards with the same title and no hint which was which: yukimemi asked why stalled and blocked appeared twice for one task, and the answer - “those are two tries, and the second one exists because of a bug since fixed” - was not on the screen anywhere.

Read from the queue rather than stored on the run, because the ordering is the queue’s fact: a RunState has no idea another attempt happened after it.

Source

pub fn superseded_by(&self, run: &str) -> Option<String>

Whether run is an earlier attempt a later one replaced, and if so the id of that later attempt.

Same walk as Queue::superseded, narrowed to one run: a run detail page asks about exactly one run at a time, and this keeps that call site from building (and discarding) the whole map’s HashMap just to read one entry out of it.

Source

pub fn latest_attempt(&self, run: &str) -> Option<String>

The task’s own most recent attempt, when run belongs to that task but is not already that attempt.

Distinct from Queue::superseded_by, which names only the very next attempt: a chain of retries (A superseded by B superseded by C) leaves an older run pointing at an intermediate one that may itself be unresolved, and a run’s own detail page needs to know where the task’s story currently stands - the chain’s current head, C - not an attempt in the middle of it that a client would otherwise have to walk to by hand.

Source

pub fn next_runnable(&self) -> Option<Task>

The task a daemon should run next, or None when the queue is idle.

Highest priority first, oldest first within a priority, so a burst of agent-filed work cannot starve the task a human filed this morning.

Source

pub fn claim(&self, id: &str) -> Result<Claim>

Take exclusive ownership of a task.

The lock is a create_new file next to the task, which is atomic on every platform magi targets. It exists so two daemons - or a daemon and a human running magi run - cannot drive one task into two competing runs. The returned guard releases on drop, including on panic.

Source

pub fn resolve_id(&self, prefix: &str) -> Result<String>

Expand an id prefix to exactly one task id.

Source

pub fn revision(&self) -> u64

Change detection token for the queue.

Combines file names and modification times of all task files in the queue, so adding, modifying, or deleting any task — even an older one — moves the revision and notifies connected clients via the change stream. Returns 0 when the queue is completely empty.

Source

pub fn revision_excluding(&self, skip: &BTreeSet<String>) -> u64

Queue::revision ignoring the files of the named task ids, for a caller that tracks those tasks’ content some other way.

Trait Implementations§

Source§

impl Clone for Queue

Source§

fn clone(&self) -> Self

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 Queue

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Queue

§

impl RefUnwindSafe for Queue

§

impl Send for Queue

§

impl Sync for Queue

§

impl Unpin for Queue

§

impl UnsafeUnpin for Queue

§

impl UnwindSafe for Queue

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
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.
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