# Plugins ## Available Plugins - `DynamicCron`: Runtime-configurable cron scheduling with cluster-wide coordination - `DynamicLifeline`: Rescues orphaned jobs, repairs stuck workflows/chains - `DynamicPrioritizer`: Automatically increases priority of waiting jobs - `DynamicPruner`: Flexible job retention with per-queue/worker rules - `DynamicQueues`: Persists queue configuration across restarts - `DynamicScaler`: Auto-scales cloud infrastructure based on queue depth ## DynamicCron Runtime-configurable cron with cluster-wide coordination. Unlike standard Oban cron, DynamicCron supports runtime updates, guaranteed scheduling, and automatic sync. ### Configuration ```elixir {Oban.Pro.Plugins.DynamicCron, timezone: "Etc/UTC", sync_mode: :automatic, crontab: [ {"*/5 * * * *", MyApp.Workers.FrequentWorker}, {"0 0 * * *", MyApp.Workers.DailyWorker, guaranteed: true}, {"0 9 * * MON", MyApp.Workers.WeeklyWorker, timezone: "America/New_York"}, {"@reboot", MyApp.Workers.StartupWorker} ]} ``` ### Options - `timezone` — default timezone for all entries (defaults to `"Etc/UTC"`) - `sync_mode` — `:automatic` syncs crontab entries to the database on startup, removing stale entries; `:manual` requires explicit `insert/1` and `delete/1` calls - `guaranteed` — per-entry option that ensures missed jobs are inserted when the system comes back online (use for critical scheduled tasks) ### Runtime Management ```elixir alias Oban.Pro.Plugins.DynamicCron # Insert or update entries at runtime DynamicCron.insert({"0 * * * *", MyApp.HourlyWorker}) # Delete an entry DynamicCron.delete(MyApp.HourlyWorker) # Get an entry's details DynamicCron.get(MyApp.HourlyWorker) # List all entries DynamicCron.all() ``` ## DynamicLifeline Rescues orphaned jobs and repairs stuck workflows and chains. Essential for production systems where nodes may crash or restart unexpectedly. ### Configuration ```elixir {Oban.Pro.Plugins.DynamicLifeline, retry_discarded: false} ``` ### Options - `retry_discarded` — whether to retry discarded workflow/chain jobs when dependencies complete Do not change the `rescue_interval` option. If you see it configured, remove it—the default is carefully tuned and changing it causes problems. ### What It Rescues - **Orphaned jobs** — jobs stuck in `executing` state after a node crash - **Stuck workflows** — workflows blocked by completed dependencies that weren't properly tracked - **Stuck chains** — chains that stopped progressing due to missed completion signals - **Partition backfill** — repopulates rate limit and global limit partitions after restarts ## DynamicPrioritizer Automatically increases priority of jobs that have been waiting too long. Prevents starvation of low-priority jobs in busy systems. ### Configuration ```elixir {Oban.Pro.Plugins.DynamicPrioritizer, prioritizers: [ rate: [queue: :default, rate: 1, period: to_timeout(second: 30)], once: [queue: :media, threshold: to_timeout(minute: 5), boost: 1] ]} ``` ### Strategies - `rate` — continuously boost priority at a fixed rate while waiting - `once` — boost priority once after a threshold wait time ### Options - `queue` — target queue (required) - `rate` — priority boost per period (for `rate` strategy) - `period` — how often to apply the boost (for `rate` strategy) - `threshold` — wait time before boosting (for `once` strategy) - `boost` — how much to increase priority (for `once` strategy, default: 1) ## DynamicPruner Flexible job retention with per-queue and per-worker rules. More powerful than the standard Oban pruner with fine-grained control over what gets deleted and when. ### Configuration ```elixir {Oban.Pro.Plugins.DynamicPruner, mode: {:max_age, {7, :days}}, queue_overrides: [ logs: {:max_age, {1, :day}}, critical: {:max_age, {30, :days}} ], worker_overrides: [ MyApp.AuditWorker: {:max_age, {90, :days}}, MyApp.TempWorker: {:max_age, {1, :hour}} ]} ``` ### Modes - `{:max_age, {count, unit}}` — delete jobs older than the specified age - `{:max_length, count}` — keep only the most recent N jobs ### Options - `mode` — default retention rule for all jobs - `queue_overrides` — per-queue retention rules - `worker_overrides` — per-worker retention rules (takes precedence over queue rules) - `state` — which job states to prune (default: `[:completed, :cancelled, :discarded]`) ## DynamicQueues Persists queue configuration to the database for runtime management. Queues survive restarts and can be modified without redeploying. ### Configuration ```elixir {Oban.Pro.Plugins.DynamicQueues, sync_mode: :automatic, queues: [ default: 10, mailers: [local_limit: 20, rate_limit: [allowed: 100, period: {1, :minute}]], exports: [local_limit: 5, only: {:node, :=~, "worker"}] ]} ``` ### Options - `sync_mode` — `:automatic` syncs queue config to database on startup; `:manual` requires explicit API calls - `queues` — initial queue definitions (same format as standard Oban queues) - `only` — restrict queue to matching nodes using `{:node, pattern}` or `{:sys_env, key, pattern}`; supports operators `:==`, `:!=`, `:=~` for pattern matching ### Runtime Management ```elixir alias Oban.Pro.Plugins.DynamicQueues # Add or update a queue DynamicQueues.insert(:reports, local_limit: 5) # Pause a queue DynamicQueues.pause(:reports) # Resume a queue DynamicQueues.resume(:reports) # Scale a queue DynamicQueues.scale(:reports, local_limit: 20) # Delete a queue DynamicQueues.delete(:reports) # List all queues DynamicQueues.all() ``` ## DynamicScaler Auto-scales cloud infrastructure based on queue depth and throughput. Scales nodes horizontally to optimize processing during high traffic and save costs during quiet periods. ### Configuration ```elixir {Oban.Pro.Plugins.DynamicScaler, scalers: [ range: 1..5, cloud: MyApp.Cloud ]} ``` ### Options - `cloud` — module implementing the `Oban.Pro.Cloud` behaviour (required) - `range` — min..max nodes to scale between (required) - `cooldown` — minimum time between scaling events (default: 120 seconds) - `queues` — `:all` or list of queues to monitor (default: `:all`) - `step` — `:none` or max nodes to scale at once (default: `:none`) ### Features - **Predictive scaling** — calculates optimal scale based on recent trends, not just current depth - **Multi-cloud** — implement the `Cloud` behaviour for your platform (AWS, Fly, Kubernetes, etc.) - **Thrashing prevention** — cooldown period prevents rapid scale up/down cycles ### Cloud Behaviour Implement `Oban.Pro.Cloud` for your platform: ```elixir defmodule MyApp.Cloud do @behaviour Oban.Pro.Cloud @impl true def init(opts), do: opts @impl true def current(_opts), do: {:ok, current_node_count()} @impl true def scale(count, _opts), do: scale_to(count) end ```