mule/jobs/job
Types
An updates list folded to one optional value per field — the analog of the
changeset’s changes map, duplicates already collapsed to the last
occurrence. Engines that render SQL consume this instead of the raw list
so a repeated field never yields two SET clauses on one column.
pub type Changes {
Changes(
args: option.Option(json.Json),
max_attempts: option.Option(Int),
meta: option.Option(json.Json),
priority: option.Option(Int),
queue: option.Option(String),
scheduled_at: option.Option(timestamp.Timestamp),
tags: option.Option(List(String)),
)
}
Constructors
-
Changes( args: option.Option(json.Json), max_attempts: option.Option(Int), meta: option.Option(json.Json), priority: option.Option(Int), queue: option.Option(String), scheduled_at: option.Option(timestamp.Timestamp), tags: option.Option(List(String)), )
pub type Job {
Job(
id: Int,
state: states.State,
queue: String,
worker: String,
args: dynamic.Dynamic,
meta: dynamic.Dynamic,
tags: List(String),
errors: List(error.Error),
attempt: Int,
attempted_by: List(String),
max_attempts: Int,
priority: Int,
inserted_at: timestamp.Timestamp,
scheduled_at: timestamp.Timestamp,
attempted_at: option.Option(timestamp.Timestamp),
cancelled_at: option.Option(timestamp.Timestamp),
completed_at: option.Option(timestamp.Timestamp),
discarded_at: option.Option(timestamp.Timestamp),
unsaved_error: option.Option(exceptions.UnsavedError),
)
}
Constructors
-
Job( id: Int, state: states.State, queue: String, worker: String, args: dynamic.Dynamic, meta: dynamic.Dynamic, tags: List(String), errors: List(error.Error), attempt: Int, attempted_by: List(String), max_attempts: Int, priority: Int, inserted_at: timestamp.Timestamp, scheduled_at: timestamp.Timestamp, attempted_at: option.Option(timestamp.Timestamp), cancelled_at: option.Option(timestamp.Timestamp), completed_at: option.Option(timestamp.Timestamp), discarded_at: option.Option(timestamp.Timestamp), unsaved_error: option.Option(exceptions.UnsavedError), )
pub type NewJob {
NewJob(
worker: String,
args: json.Json,
queue: String,
meta: json.Json,
tags: List(String),
max_attempts: Int,
priority: Int,
scheduled_at: option.Option(timestamp.Timestamp),
unique: option.Option(UniqueOptions),
replace: List(#(states.State, List(ReplaceField))),
)
}
Constructors
-
NewJob( worker: String, args: json.Json, queue: String, meta: json.Json, tags: List(String), max_attempts: Int, priority: Int, scheduled_at: option.Option(timestamp.Timestamp), unique: option.Option(UniqueOptions), replace: List(#(states.State, List(ReplaceField))), )
Mirrors Oban.Job.option() (job.ex): the keyword options accepted by
Job.new/2, minus worker/args (positional here).
pub type Option {
MaxAttempts(max_attempts: Int)
Meta(meta: json.Json)
Priority(priority: Int)
Queue(queue: String)
Replace(replace: List(#(states.State, List(ReplaceField))))
ScheduleIn(schedule_in: duration.Duration)
ScheduledAt(scheduled_at: timestamp.Timestamp)
Tags(tags: List(String))
Unique(unique: List(UniqueOption))
}
Constructors
-
MaxAttempts(max_attempts: Int) -
Meta(meta: json.Json)metamust be a JSON object: the uniquenesskeyspath runsjsonb_eachover it, and non-object jsonb breaks containment matching. -
Priority(priority: Int) -
Queue(queue: String) -
Replace(replace: List(#(states.State, List(ReplaceField))))Per-state conflict resolution for a unique insert (job.ex’s keyword
replaceform): onConflict, overwrite the listed fields of the existing row — scoped by the state that row is in — with this insert’s values.replace_in_all_states/replace_argsbuild the flat-list andreplace_args: trueshorthands. A duplicate state keeps the FIRST entry (Elixir’sKeyword.get). -
ScheduleIn(schedule_in: duration.Duration)Relative scheduling: resolved to
scheduled_at = now + schedule_inat construction time, and overriding anyScheduledAtregardless of list order (job.ex appliesput_schedulingafter cast). -
ScheduledAt(scheduled_at: timestamp.Timestamp) -
Tags(tags: List(String)) -
Unique(unique: List(UniqueOption))Unique sub-options folded over
unique_defaults()(resolve_unique) — the typed analog of job.ex’sunique: [keyword]merged into@unique_defaults.Unique([])is therefore the bare defaults; Elixir’sunique: trueadditionally sets an infinite period, so its analog isUnique([UniquePeriod(Infinity)]).
The typed analog of Oban.Job.query/1’s keyword filters. Each set field is
a conjunct (all must match); None means unfiltered. List fields are IN
matches, tags compares the complete array, args/meta are one-way
jsonb containment (@>). Built pipeline-style from query() through the
filter_* combinators; a repeated combinator call replaces the earlier
value for that field. limit caps how many rows an operation touches (the
analog of composing Ecto’s limit onto the queryable), applied after the
deterministic order-by-id.
pub type Query {
Query(
ids: option.Option(List(Int)),
states: option.Option(List(states.State)),
queues: option.Option(List(String)),
workers: option.Option(List(String)),
priorities: option.Option(List(Int)),
tags: option.Option(List(String)),
args: option.Option(json.Json),
meta: option.Option(json.Json),
limit: option.Option(Int),
)
}
Constructors
-
Query( ids: option.Option(List(Int)), states: option.Option(List(states.State)), queues: option.Option(List(String)), workers: option.Option(List(String)), priorities: option.Option(List(Int)), tags: option.Option(List(String)), args: option.Option(json.Json), meta: option.Option(json.Json), limit: option.Option(Int), )
Mirrors job.ex’s @replace_options: the existing row’s fields a unique
Conflict may overwrite with the conflicting insert’s values.
pub type ReplaceField {
ReplaceArgs
ReplaceMaxAttempts
ReplaceMeta
ReplacePriority
ReplaceQueue
ReplaceScheduledAt
ReplaceTags
ReplaceWorker
}
Constructors
-
ReplaceArgs -
ReplaceMaxAttempts -
ReplaceMeta -
ReplacePriority -
ReplaceQueue -
ReplaceScheduledAt -
ReplaceTags -
ReplaceWorker
pub type UniqueField {
ByWorker
ByQueue
ByArgs
ByMeta
}
Constructors
-
ByWorker -
ByQueue -
ByArgs -
ByMeta
One unique: [...] keyword — the partial form the Unique option and
worker defaults carry, so worker.merge_options can deep-merge a worker’s
unique sub-options with a caller’s (Worker.merge_opts/2’s :unique
clause) instead of replacing the whole record.
pub type UniqueOption {
UniqueFields(fields: List(UniqueField))
UniqueKeys(keys: List(String))
UniquePeriod(period: UniquePeriod)
UniqueStates(states: set.Set(states.State))
UniqueTimestamp(timestamp: UniqueTimestamp)
}
Constructors
-
UniqueFields(fields: List(UniqueField)) -
UniqueKeys(keys: List(String)) -
UniquePeriod(period: UniquePeriod) -
UniqueStates(states: set.Set(states.State)) -
UniqueTimestamp(timestamp: UniqueTimestamp)
Mirrors job.ex’s @unique_defaults shape. keys narrows args/meta
matching to the named top-level JSON keys (Elixir’s atom keys are strings
here); period bounds how far back a match may lie, measured against
timestamp.
pub type UniqueOptions {
UniqueOptions(
fields: List(UniqueField),
keys: List(String),
period: UniquePeriod,
states: set.Set(states.State),
timestamp: UniqueTimestamp,
)
}
Constructors
-
UniqueOptions( fields: List(UniqueField), keys: List(String), period: UniquePeriod, states: set.Set(states.State), timestamp: UniqueTimestamp, )
Mirrors unique_period(): a finite window or :infinity.
pub type UniquePeriod {
Period(window: duration.Duration)
Infinity
}
Constructors
-
Period(window: duration.Duration) -
Infinity
Mirrors Oban.Job.unique_states/1’s named groups.
pub type UniqueStateGroup {
All
Incomplete
Scheduled
Successful
}
Constructors
-
All -
Incomplete -
Scheduled -
Successful
Which timestamp the period window is measured against
(mirrors unique_timestamp(): :inserted_at | :scheduled_at).
By* prefix because a bare ScheduledAt constructor would collide with
the Option variant in this module.
pub type UniqueTimestamp {
ByInsertedAt
ByScheduledAt
}
Constructors
-
ByInsertedAt -
ByScheduledAt
Mirrors job.ex’s @updatable_params (minus worker — the port pairs a
worker’s name with its typed args codec, so retargeting the name alone is
deliberately unrepresentable): the fields mule.update_job may change.
A duplicate keeps the last occurrence, like new_with options.
pub type Update {
UpdateArgs(args: json.Json)
UpdateMaxAttempts(max_attempts: Int)
UpdateMeta(meta: json.Json)
UpdatePriority(priority: Int)
UpdateQueue(queue: String)
UpdateScheduledAt(scheduled_at: timestamp.Timestamp)
UpdateTags(tags: List(String))
}
Constructors
-
UpdateArgs(args: json.Json) -
UpdateMaxAttempts(max_attempts: Int) -
UpdateMeta(meta: json.Json) -
UpdatePriority(priority: Int) -
UpdateQueue(queue: String) -
UpdateScheduledAt(scheduled_at: timestamp.Timestamp) -
UpdateTags(tags: List(String))
One failed new_with validation: the field that failed and why (the
analog of an Ecto changeset error from Job.new/2).
pub type ValidationError {
ValidationError(field: String, message: String)
}
Constructors
-
ValidationError(field: String, message: String)
Values
pub fn filter_args(
query query: Query,
args args: json.Json,
) -> Query
One-way jsonb containment (args @> filter) — NOT the mutual containment
the unique-insert path uses. An empty object {} therefore matches every
job (true @> semantics; Elixir raises on an empty filter instead).
pub fn filter_id(query query: Query, ids ids: List(Int)) -> Query
An empty list matches nothing — the typed, total-function analog of Elixir
raising ArgumentError on empty filter lists. The same holds for the other
list combinators below (except filter_tags).
pub fn filter_meta(
query query: Query,
meta meta: json.Json,
) -> Query
One-way jsonb containment (meta @> filter); see filter_args.
pub fn filter_state(
query query: Query,
states states: List(states.State),
) -> Query
pub fn filter_tags(
query query: Query,
tags tags: List(String),
) -> Query
Compares the complete tags array, element-order-sensitive, exactly like
Elixir’s job.tags == ^value — so filter_tags(query, []) legitimately
matches jobs with no tags.
pub fn format_attempt(job job: Job) -> option.Option(error.Error)
Flatten unsaved_error into the persisted errors-column shape
(job.ex format_attempt/1). None when no unsaved error is recorded.
pub fn json_recode(value value: json.Json) -> dynamic.Dynamic
Mirror the SQL wire path (Elixir’s json_recode in inline.ex/testing.ex):
encode the JSON, parse it back to the opaque Dynamic the read side
carries — an unpersisted job’s args/meta end up shaped exactly like a
stored row’s. Safe assert: the JSON was just produced by the caller’s
encoder. Shared by this module’s update path, the in-memory and inline
engines, and mule/testing.
pub fn limit(query query: Query, limit limit: Int) -> Query
Cap how many rows the operation touches, in id order (the analog of
composing Ecto’s limit(n) onto Job.query/1).
pub fn new_with(
worker worker: String,
args args: json.Json,
options options: List(Option),
) -> Result(NewJob, ValidationError)
Job.new/2 with options: fold each option over the defaults (a duplicate
option keeps the last occurrence, like Map.new(opts)), then validate the
same bounds job.ex does — worker/queue length 1–128, max_attempts > 0,
priority 0–9.
pub fn replace_args() -> Option
The replace_args: true shorthand: replace args in every state.
pub fn replace_in_all_states(
fields fields: List(ReplaceField),
) -> Option
The flat-list replace: [fields] form: the same fields in every state
(job.ex put_replace’s with_states).
pub fn resolve_unique(
options options: List(UniqueOption),
) -> UniqueOptions
Fold unique sub-options over unique_defaults(), a duplicate keeping the
last — job.ex put_unique’s @unique_defaults |> Map.merge(Map.new(opts)).
pub fn unique_defaults() -> UniqueOptions
Mirrors job.ex’s @unique_defaults. Note the states are NOT
unique_states(Successful) — Elixir’s default set lacks suspended.
pub fn unique_states(
group: UniqueStateGroup,
) -> set.Set(states.State)
pub fn update(
job job: Job,
updates updates: List(Update),
) -> Result(Job, ValidationError)
Job.update/2: fold validated changes over the job. UpdateScheduledAt
also moves the job to scheduled — job.ex’s update never casts state,
so normalize_state fires on ANY scheduled_at change.
pub fn validate_changes(
changes changes: Changes,
) -> Result(Changes, ValidationError)
Job.update/2’s validations — the same bounds new_with checks, applied
to only the changed fields: queue length 1–128, max_attempts > 0,
priority 0–9. Tags are NOT normalized (job.ex’s update casts them
without normalize_tags).