Current section

59 Versions

Jump to

Compare versions

13 files changed
+644 additions
-87 deletions
  @@ -38,6 +38,7 @@ spark_locals_without_parens = [
38 38 state: 1,
39 39 stream_batch_size: 1,
40 40 stream_with: 1,
41 + tags: 1,
41 42 timeout: 1,
42 43 trigger: 1,
43 44 trigger: 2,
  @@ -11,6 +11,23 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
11 11
12 12 <!-- changelog -->
13 13
14 + ## [v0.8.0-rc.1](https://github.com/ash-project/ash_oban/compare/v0.8.0-rc.0...v0.8.0-rc.1) (2026-02-23)
15 +
16 +
17 +
18 +
19 + ### Improvements:
20 +
21 + * add new test helpers for triggers & schedulers by Zach Daniel
22 +
23 + * add support for snoozing and cancelling Oban jobs from actions by Zach Daniel
24 +
25 + * validate that `on_error` action exists by Zach Daniel
26 +
27 + * support setting tags in DSL by Zach Daniel
28 +
29 + * set job into context to allow for better logging and error handling by Zach Daniel
30 +
14 31 ## [v0.8.0-rc.0](https://github.com/ash-project/ash_oban/compare/v0.7.2...v0.8.0-rc.0) (2026-02-22)
  @@ -150,6 +150,7 @@ end
150 150 | [`on_error_fails_job?`](#oban-triggers-trigger-on_error_fails_job?){: #oban-triggers-trigger-on_error_fails_job? } | `boolean` | `false` | Determines if the oban job will be failed on the last attempt when there is an on_error handler that is called. If there is no on_error, then the action is always marked as failed on the last attempt. |
151 151 | [`shared_context?`](#oban-triggers-trigger-shared_context?){: #oban-triggers-trigger-shared_context? } | `boolean` | | If set to `true`, the `ash_oban?: true` flag will be placed in shared context instead of regular context. Shared context propagates to related actions called via `manage_relationship` and can be passed to other action invocations using the context as scope, making it easier to detect AshOban execution in nested actions. If not specified, inherits the global `shared_context?` setting from the `oban` section. |
152 152 | [`use_tenant_from_record?`](#oban-triggers-trigger-use_tenant_from_record?){: #oban-triggers-trigger-use_tenant_from_record? } | `boolean` | `false` | If set to `true`, the tenant will be extracted from each record's tenant attribute and used when the worker processes that record. This allows the scheduler to use a multitenancy `:allow_global` read action to find records across all tenants, while the worker action still runs with the correct tenant context for each individual record. If not specified, inherits the global `use_tenant_from_record?` setting from the `oban` section. |
153 + | [`tags`](#oban-triggers-trigger-tags){: #oban-triggers-trigger-tags } | `list(String.t)` | `[]` | A list of tags to add to the Oban job. Tags are merged with any tags set in `worker_opts`. |
153 154 | [`worker_opts`](#oban-triggers-trigger-worker_opts){: #oban-triggers-trigger-worker_opts } | `keyword` | `[]` | Options to set on the worker. ATTENTION: this may overwrite options set by ash_oban, make sure you know what you are doing. See [Oban.Worker](https://hexdocs.pm/oban/Oban.Worker.html#module-defining-workers) for options and [Oban.Pro.Worker](https://oban.pro/docs/pro/Oban.Pro.Worker.html) for oban pro |
154 155 | [`backoff`](#oban-triggers-trigger-backoff){: #oban-triggers-trigger-backoff } | `pos_integer \| (any -> any) \| :exponential` | `:exponential` | Configure after how much time job should (in seconds) be retried in case of error if more retries available. Can be a number of seconds or a function that takes the job and returns a number of seconds. Will not be executed if default max_attempts value of 1 will be used. See [Oban.Worker](https://hexdocs.pm/oban/Oban.Worker.html#module-customizing-backoff) for more about backoff. backoff 10 backoff fn _job -> 10 end backoff fn %Oban.Job{attempt: attempt} -> 10 * attempt end backoff fn %Oban.Job{attempt: attempt, unsaved_error: unsaved_error} -> %{kind: _, reason: reason, stacktrace: _} = unsaved_error case reason do %MyApp.ApiError{status: 429} -> 300 _ -> trunc(:math.pow(attempt, 4)) end end |
155 156 | [`timeout`](#oban-triggers-trigger-timeout){: #oban-triggers-trigger-timeout } | `pos_integer \| (any -> any) \| :infinity` | `:infinity` | Configure timeout for the job in milliseconds. See [Oban.Worker timeout](https://hexdocs.pm/oban/Oban.Worker.html#module-customizing-timeout) for more about timeout. timeout 30_000 timeout fn _job -> :timer.seconds(30) end |
  @@ -269,6 +270,7 @@ schedule name, cron
269 270 | [`state`](#oban-scheduled_actions-schedule-state){: #oban-scheduled_actions-schedule-state } | `:active \| :paused \| :deleted` | `:active` | Describes the state of the cron job. See the getting started guide for more information. The most important thing is that you *do not remove a scheduled action from a resource if you are using oban pro*. |
270 271 | [`max_attempts`](#oban-scheduled_actions-schedule-max_attempts){: #oban-scheduled_actions-schedule-max_attempts } | `pos_integer` | `1` | How many times to attempt the job. The action will receive a `last_oban_attempt?` argument on the last attempt, and you should handle errors accordingly. |
271 272 | [`priority`](#oban-scheduled_actions-schedule-priority){: #oban-scheduled_actions-schedule-priority } | `non_neg_integer` | `3` | A number from 0 to 3, where 0 is the highest priority and 3 is the lowest. |
273 + | [`tags`](#oban-scheduled_actions-schedule-tags){: #oban-scheduled_actions-schedule-tags } | `list(String.t)` | `[]` | A list of tags to add to the Oban job. |
272 274 | [`debug?`](#oban-scheduled_actions-schedule-debug?){: #oban-scheduled_actions-schedule-debug? } | `boolean` | `false` | If set to `true`, detailed debug logging will be enabled for this trigger. You can also set `config :ash_oban, debug_all_triggers?: true` to enable debug logging for all triggers. |
273 275 | [`shared_context?`](#oban-scheduled_actions-schedule-shared_context?){: #oban-scheduled_actions-schedule-shared_context? } | `boolean` | | If set to `true`, the `ash_oban?: true` flag will be placed in shared context instead of regular context. Shared context propagates to related actions called via `manage_relationship` and can be passed to other action invocations using the context as scope, making it easier to detect AshOban execution in nested actions. If not specified, inherits the global `shared_context?` setting from the `oban` section. |
  @@ -149,6 +149,124 @@ A chunk counts as **one** job execution against Oban's queue concurrency and rat
149 149 - Only works with `update` and `destroy` actions (not generic or create actions).
150 150 - Atomic actions are recommended for efficient bulk execution, though non-atomic actions are supported via the `:stream` strategy.
151 151
152 + ### Tagging Jobs
153 +
154 + You can attach tags to jobs for filtering and grouping in telemetry, notifications, and the Oban dashboard:
155 +
156 + ```elixir
157 + trigger :process do
158 + action :process
159 + where expr(processed != true)
160 + tags ["processing", "intense"]
161 + end
162 +
163 + schedule :import_from_github, "0 */6 * * *" do
164 + action :import_from_github
165 + tags ["import", "external"]
166 + end
167 + ```
168 +
169 + Tags set via `tags` are merged with any tags you also set in `worker_opts`, so both lists are combined.
170 +
171 + ### Accessing the Oban Job
172 +
173 + The underlying `%Oban.Job{}` struct is available in the context of any action run by a trigger or scheduled action. Access it via `context.ash_oban.job`:
174 +
175 + ```elixir
176 + # In a change module
177 + def change(changeset, _opts, context) do
178 + job = context.ash_oban.job
179 + Ash.Changeset.put_context(changeset, :oban_tags, job.tags)
180 + end
181 +
182 + # In a generic action implementation
183 + def run(input, _opts, context) do
184 + job = context.ash_oban.job
185 + Logger.info("Attempt #{job.attempt} of #{job.max_attempts}")
186 + :ok
187 + end
188 + ```
189 +
190 + > ### Chunk processing {: .info}
191 + > The `oban_job` is not available in chunk worker contexts, since those process a batch of jobs rather than a single one.
192 +
193 + ### Controlling Job Behavior (Snooze and Cancel)
194 +
195 + From within any action run by a trigger or scheduled action, you can snooze or cancel the Oban job by raising (or returning as an error) `AshOban.Errors.SnoozeJob` or `AshOban.Errors.CancelJob`. These work from both the main action and the `on_error` action.
196 +
197 + #### Snoozing a job
198 +
199 + Snoozing re-schedules the job to run again after a delay without consuming an attempt. Use this for transient conditions like rate limits or temporary unavailability:
200 +
201 + ```elixir
202 + update :sync_with_external_api do
203 + change fn changeset, _context ->
204 + case ExternalAPI.rate_limit_remaining() do
205 + 0 ->
206 + Ash.Changeset.add_error(
207 + changeset,
208 + AshOban.Errors.SnoozeJob.exception(snooze_for: 60)
209 + )
210 + _ ->
211 + do_sync(changeset)
212 + end
213 + end
214 + end
215 + ```
216 +
217 + Or via `raise`:
218 +
219 + ```elixir
220 + raise AshOban.Errors.SnoozeJob, snooze_for: 60
221 + ```
222 +
223 + The `snooze_for` field is an integer number of seconds.
224 +
225 + #### Cancelling a job
226 +
227 + Cancelling stops all retries and marks the job as cancelled. Use this when you determine that further processing is permanently invalid:
228 +
229 + ```elixir
230 + update :process do
231 + change fn changeset, _context ->
232 + record = changeset.data
233 +
234 + if record.deleted_at do
235 + Ash.Changeset.add_error(
236 + changeset,
237 + AshOban.Errors.CancelJob.exception(reason: :record_deleted)
238 + )
239 + else
240 + do_process(changeset)
241 + end
242 + end
243 + end
244 + ```
245 +
246 + Or via `raise`:
247 +
248 + ```elixir
249 + raise AshOban.Errors.CancelJob, reason: :permanently_invalid
250 + ```
251 +
252 + The `reason` field accepts any term.
253 +
254 + #### From the on_error action
255 +
256 + Both errors also work from the `on_error` action. For example, to cancel the job from your error handler rather than failing it:
257 +
258 + ```elixir
259 + update :handle_failure do
260 + change fn changeset, _context ->
261 + # Log the failure, then cancel the job instead of failing it
262 + Ash.Changeset.add_error(
263 + changeset,
264 + AshOban.Errors.CancelJob.exception(reason: :handled)
265 + )
266 + end
267 + end
268 + ```
269 +
152 270 ### Scheduled Actions
153 271
154 272 Scheduled actions are a much simpler concept than triggers. They are used to perform a generic action on a specified schedule. For example, lets say
  @@ -18,7 +18,7 @@ about what `AshOban` provides.
18 18 ## Bring in the `ash_oban` dependency
19 19
20 20 ```elixir
21 - {:ash_oban, "~> 0.8.0-rc.0"}
21 + {:ash_oban, "~> 0.8.0-rc.1"}
22 22 ```
23 23
24 24 ## Setup
Loading more files…