v0.146.0

plain-jobs changelog

0.54.0 (2026-05-20)

What's changed

  • Deferred jobs whose re-enqueue is blocked by should_enqueue() now skip silently instead of erroring. Previously, raising DeferJob inside run() would attempt to re-enqueue, and if a peer existed or a custom rule rejected the new request, the framework raised DeferError and recorded the result as ERRORED. That treated normal concurrency / rate-limit outcomes as failures and surfaced them to exception tooling. The framework now honors the should_enqueue() signal the same way run_in_worker() and retry_job() already do: the JobResult is recorded as DEFERRED with retry_job_request_uuid=None, the error message documents that the re-enqueue was skipped, and the consumer span gets plain.jobs.defer.skipped=True so the case is queryable in APM without surfacing as an exception. (3171bb4238)
  • DeferError removed from the public API. Nothing raises it anymore.

Upgrade instructions

  • Remove any except DeferError: blocks. To detect deferred re-enqueues that were skipped, query for JobResult rows with status=DEFERRED and retry_job_request_uuid IS NULL, or alert on the plain.jobs.defer.skipped=True span attribute.

0.53.1 (2026-05-19)

What's changed

  • Background loops no longer wedge on a dead database connection. The worker maintenance loop and the OTel observable-gauge callbacks each ran on a long-lived thread that held one pooled connection idle between ticks/export intervals — long enough for the server or pooler to close it. The next tick then reused the dead connection and raised OperationalError: the connection is closed, with no path to recover. The worker loop now returns its connection at the start of every tick, and the five DB-touching gauge callbacks return theirs once each observation is collected, so the next checkout always starts from a fresh, validated connection. (31ad84f423)

Upgrade instructions

  • No changes required.

0.53.0 (2026-05-12)

What's changed

  • Worker maintenance loop is now wrapped in an OTel span. Previously, DB transients during heartbeat/rescue/schedule work were swallowed by the outer except Exception with logger.exception only — invisible to OTel-based exception tooling. The new worker loop INTERNAL span stamps status=ERROR + error.type on failed iterations while preserving the log-and-continue behavior. (c72ebe74bd)
  • Job-lookup failures in process_job now get a fallback CONSUMER span. Previously a psycopg transient during the row lookup (before JobProcess.run() started its own span) only left a CLIENT span — invisible under entry-span error attribution. The new wrapper span captures lookup-time failures and stays out of the way on the success path. The worker maintenance loop kind also flipped from INTERNALCONSUMER to match the chore convention. (f4444949c7)
  • exception.escaped no longer emitted on framework spans. The OTel semconv attribute is deprecated upstream (semconv PR #1716) and the Python SDK records it inconsistently. status=ERROR + error.type is the canonical signal now. (bb9251f165)
  • Pins plain>=0.143.0.

Upgrade instructions

  • No code changes required.
  • If your dashboards or alerts filter on exception.escaped, switch to filtering on span.status_code = 'ERROR' + error.type instead.

0.52.2 (2026-05-06)

What's changed

  • Removed four redundant single-column indexes shadowed by unique constraints. JobRequest.uuid, JobProcess.uuid, JobResult.uuid, and JobResult.job_process_uuid each carried both a UniqueConstraint (which Postgres backs with an implicit btree) and a separately-declared Index(fields=["..."]) on the same column. The non-unique index was pure write-side overhead — every insert paid a duplicate btree maintenance cost on indexes the planner could already satisfy from the unique-backed btree. Convergence drops the redundant indexes on the next plain postgres sync. (48d850ca)

Upgrade instructions

  • No changes required. Run plain postgres sync after upgrading; convergence will issue DROP INDEX CONCURRENTLY for the four redundant indexes (plainjobs_jobrequest_uuid_idx, plainjobs_jobprocess_uuid_idx, plainjobs_jobresult_uuid_idx, plainjobs_jobresult_job_process_uuid_idx) and existing UUID lookups will use the unique-backed btree instead.

0.52.1 (2026-05-06)

What's changed

  • Failed-enqueue (PRODUCER) and failed-job (CONSUMER) span exceptions now carry exception.escaped=True. The OTel SDK's auto-record path stamps escaped=False, which made the attribute useless for distinguishing workflow-level failures from internal child-span exceptions. The PRODUCER path now passes record_exception=False to suppress the SDK auto-record and routes through record_span_error so the failed send carries a single, correctly-marked event with error.type on the span (matching CONSUMER). (e9c35b855f)

Upgrade instructions

  • No changes required.

0.52.0 (2026-05-06)

What's changed

  • messaging.client.consumed.messages now carries a plain.jobs.outcome attribute (successful, errored, lost, cancelled, deferred) so dashboards can split throughput by terminal status. error.type is also forwarded for errored jobs caught by the live path. The deferred path now ticks the counter too — previously it bypassed the increment because defer() skips convert_to_result. (49fa1952)
  • New plain.jobs.workers observable gauge reporting WorkerHeartbeat row count split by a plain.jobs.worker.state attribute (active within JOBS_HEARTBEAT_TIMEOUT, stale past it). Global gauge with no per-queue dimension — aggregate with last_value/max, never sum. (49fa1952)

Upgrade instructions

  • If you have dashboards aggregating messaging.client.consumed.messages, sum across the new plain.jobs.outcome dimension to keep the existing total — or split by outcome to get the new view.

0.51.0 (2026-05-06)

What's changed

  • Worker liveness is now heartbeat-based, not time-based. A new WorkerHeartbeat table tracks each running worker; rescue triggers when a worker stops refreshing its heartbeat for JOBS_HEARTBEAT_TIMEOUT (default 5 min). Replaces the old JOBS_TIMEOUT design, which was unsound for jobs whose runtime might exceed the timeout — a legitimately slow job past the cutoff would get marked LOST while still running, producing duplicate JobResult rows and phantom retries. Heartbeats let the framework distinguish "still running" from "worker died" with no false positives. Detection is now minutes instead of up to a day. (8709e9a5b5)
  • New Job.on_aborted(self, result) lifecycle hook. Called for LOST and CANCELLED terminal statuses, giving apps a clean seam to reconcile domain state that run()'s try/finally would have released (release locks, flip user-visible status flags, send notifications, etc.). Fires after the rescue commit, with a fully-committed JobResult argument. Hooks that raise are logged and swallowed — they can't poison framework bookkeeping. Default implementation is a no-op.
  • JobProcess.worker_id is NOT NULL. Every in-flight row has a guaranteed owner; rescue logic doesn't need NULL-handling. Old workers attempting to claim a JobRequest after the migration runs fail loudly on the constraint instead of silently inserting unrescuable rows — the work stays queued for a new worker.
  • Killed-mid-run subprocesses (OOM/segfault) are now classified LOST instead of ERRORED when started_at was already set, so on_aborted fires for cleanup. Subprocess failures before run() started (e.g. import errors) remain ERRORED, since there's no in-flight user state.
  • Self-rescue: workers periodically reconcile their own in-flight futures against DB rows. Anything stamped to this worker that isn't tracked by a live future and is older than JOBS_HEARTBEAT_TIMEOUT gets converted to LOST. Catches the rare case where a done-callback raises during conversion.
  • on_aborted dispatch interleaves heartbeats so a slow batch of hooks can't starve this worker's heartbeat and trigger a false-positive LOST from a peer rescuer.
  • New settings: JOBS_HEARTBEAT_INTERVAL (default 60s) and JOBS_HEARTBEAT_TIMEOUT (default 300s). Removed: JOBS_TIMEOUT.
  • New admin view: WorkerHeartbeat listing with Active / Stale filters, surfacing live workers and their queue assignments.

Upgrade instructions

  • Run plain migrate to apply migration 0012_workerheartbeat_jobprocess_worker_id (creates WorkerHeartbeat, adds worker_id to JobProcess, backfills pre-existing rows under a 24h grace heartbeat). Drain workers before deploying. Old workers running pre-heartbeat code can't claim new work after the migration runs (the new NOT NULL constraint trips), so any unclaimed JobRequests wait in queue for a new worker — strictly safe, but the deploy goes smoother if old workers have finished first.
  • If you previously set PLAIN_JOBS_TIMEOUT, replace it with PLAIN_JOBS_HEARTBEAT_TIMEOUT (the analogous "how long until a stuck worker's jobs are marked LOST" knob). Default is now 5 minutes vs the old 1 day — most apps will want to leave it at the default.
  • If any of your Job subclasses relied on a crashed-worker job ending up as ERRORED, update to handle LOST and consider implementing on_aborted for cleanup.

0.50.1 (2026-05-05)

What's changed

  • Exposes __version__ from importlib.metadata on plain.jobs for version probes that don't want to scrape pip metadata. (c6cf6edb)

Upgrade instructions

  • No changes required.

0.50.0 (2026-05-01)

What's changed

  • Added plain.jobs.queue.wait.duration histogram. Each time a worker picks up a job, the time it spent waiting in the queue (between enqueue and execution start) is recorded as a histogram in seconds, with the same messaging-semconv attributes as the other plain.jobs metrics. (9ab6298fa9ae)
  • Added per-Worker observable gauges via WorkerMetrics. Each running worker now exports plain.jobs.worker.processes, plain.jobs.queue.depth, plain.jobs.queue.oldest.age, plain.jobs.queue.scheduled, and plain.jobs.running as observable gauges, queryable per messaging.destination.name. Empty queues report zero so dashboards using last_value don't show stale readings; when multiple workers cover the same queue, aggregate with last_value/max (never sum). (e08b25d38015)
  • Track job queue latency with a new requested_at field on JobProcess and JobResult. The field is copied from the originating JobRequest.created_at, used to compute queue-wait duration when a worker starts the job, and included in the "Completed job" log line as job_queue_time (seconds). New migration 0011_jobprocess_requested_at_jobresult_requested_at adds the columns (nullable, so existing in-flight jobs continue to work). (bccb8e835400)
  • Added JobRequestQuerySet with ready_to_run() and scheduled() helpers; the worker's job-pickup query now uses ready_to_run() so the same filter is reused by the queue-depth and queue-age gauges.

Upgrade instructions

  • Run plain migrate to apply the new requested_at columns on JobProcess and JobResult.

0.49.1 (2026-04-30)

What's changed

  • Job-result detail "Retry job" button uses the namespaced admin-btn admin-btn-primary classes so it renders as a primary button under plain-admin 0.81.0's namespaced classes. (5f86c86fb7e9)

Upgrade instructions

  • If you use plain.admin, upgrade it to >=0.81.0 alongside this release for the namespaced admin- CSS classes referenced by the job-result template.

0.49.0 (2026-04-30)

What's changed

  • Documented JOBS_SCHEDULE as the canonical way to configure scheduled jobs. The README's "Scheduled jobs" section now leads with the settings-based pattern — a list of (job, schedule) tuples where the job is a dotted path (or "cmd:<shell command>") and the schedule is a cron expression or Schedule instance — instead of the older class-attribute pattern. (b0cd10e71613)
  • Job admin chart colors switched to CSS tokens. The JobResultsTrendCard group colors now use var(--success), var(--danger), var(--muted-foreground), var(--info), and var(--warning) instead of hardcoded RGBA tuples, so the chart retheme automatically with plain-admin 0.80.0's new theming system (light/dark mode + brand overrides). (3e4d76259e42)

Upgrade instructions

  • If you use plain.admin, upgrade it to >=0.80.0 alongside this release. The jobs admin chart now references the CSS tokens (--success, --danger, etc.) that ship with the new admin component layer; on older admin releases the tokens won't resolve.

0.48.1 (2026-04-27)

What's changed

  • Documented the OpenTelemetry metric contract in the README. Two contract details now spelled out: successful enqueues record metrics on transaction commit (matching the OTel semconv: "MUST NOT count messages that were created but haven't yet been sent"), and skipped enqueues — when should_enqueue returns False — are visible on the span via job.enqueue.skipped but do not fire the messaging counter. Also clarified that send / process spans use the messaging semconv naming. No behavior change. (2a106fbad26c)

Upgrade instructions

  • No changes required.

0.48.0 (2026-04-24)

What's changed

  • Added OpenTelemetry messaging semantic-convention metrics. plain.jobs now records messaging.client.sent.messages, messaging.client.consumed.messages, and messaging.client.operation.duration around enqueue and process. Metric attributes follow the messaging semconv (messaging.system=plain.jobs, messaging.operation.type, messaging.destination.name, code.function.name), and errors are tagged with error.type. A new plain.jobs.otel module holds the shared tracer, meter, metric instruments, and a record_span_error helper. (6fc7c4e04910)
  • Switched enqueue source capture from inspect.stack() to sys._getframe(1) — same source (filename:lineno) attribute on the producer span, without the full stack walk. (aabc7d7fe97f)

Upgrade instructions

  • No changes required.

0.47.6 (2026-04-22)

What's changed

  • Replaced the request_started / request_finished signal calls around process_job() with an explicit return_database_connection() in the finally block. Matches the new pool-backed connection lifecycle in plain-postgres 0.98.0 and follows the removal of plain.signals in plain 0.134.0. (2a51b25)

Upgrade instructions

  • Requires plain>=0.134.0 and plain-postgres>=0.98.0.

0.47.5 (2026-04-17)

What's changed

  • Updated JobRequest, JobProcess, and JobResult to use the new plain-postgres 0.96.0 field API: UUIDField(generate=True) (Postgres-side gen_random_uuid() per row) and DateTimeField(create_now=True). Dropped the uuid4 import. (a44e5ec, 5d145e4)

Upgrade instructions

  • Requires plain-postgres>=0.96.0. Run plain postgres sync after upgrading to reconcile column defaults.

0.47.4 (2026-04-14)

What's changed

  • Updated jobs clear and jobs purge CLI commands and the ClearCompleted chore to use the new QuerySet.delete() return type (an int directly instead of a (count, by_label) tuple) from plain-postgres 0.95.0. (29e10dba51d9)
  • Raised plain.postgres floor to >=0.95.0.

Upgrade instructions

  • Requires plain-postgres>=0.95.0.

0.47.3 (2026-04-13)

What's changed

  • Migrated type suppression comments to ty: ignore for the new ty checker version. (4ec631a7ef51)

Upgrade instructions

  • No changes required.

0.47.2 (2026-04-05)

What's changed

  • Job span names now follow OTel messaging semconv. Producer spans are named send {queue} (was run_in_worker {job_class}), consumer spans are named process {queue} (was run {job_class}). The job class name is now in code.function.name instead of the span name. (b56a9edc9c7d)
  • Swapped deprecated code attribute constants. code.filepathcode.file.path, code.linenocode.line.number, code.namespace → fully-qualified code.function.name. (b56a9edc9c7d)
  • Added TraceFlags(SAMPLED) to job trace links. Links from consumer spans back to the producing trace now correctly set the sampled flag. (b56a9edc9c7d)
  • Changed ShouldNotEnqueue from error.type to job.enqueue.skipped boolean. Skipping an enqueue due to concurrency control is normal flow, not an error. (b56a9edc9c7d)
  • Removed set_status(OK) from job spans. Per the OTel spec, instrumentation libraries should leave span status as Unset on success. (b56a9edc9c7d)
  • error.type now uses fully-qualified exception class names. Uses format_exception_type() for consistent naming across packages. (b56a9edc9c7d)

Upgrade instructions

  • No changes required.

0.47.1 (2026-03-29)

What's changed

  • Removed AddIndex, RenameIndex, and AddConstraint operations from migrations — indexes and constraints are now managed by convergence. (c58b4ba1fec9, 1f15538b008f)
  • Updated docs to reference plain postgres sync instead of plain migrate. (b026895edc4c)

Upgrade instructions

  • Requires plain-postgres>=0.91.0.

0.47.0 (2026-03-28)

What's changed

  • Replaced CharField with TextField in models and migration files to match plain-postgres 0.90.0 (5062ee4dd1fd)

Upgrade instructions

  • Requires plain-postgres>=0.90.0
  • Replace CharField with TextField in migration files that reference this package's models

0.46.6 (2026-03-27)

What's changed

  • Jobs are now re-enqueued when the process pool breaks — if a child process crashes (OOM, segfault) or the executor shuts down during submit, the job is reverted from JobProcess back to JobRequest instead of being lost (984cfe724da7)
  • Changed jobs purge confirmation flag to --yes/-y for consistency across all CLI commands (0af36e101f03)

Upgrade instructions

  • If you use plain jobs purge in scripts without confirmation, add --yes to skip the prompt.

0.46.5 (2026-03-25)

What's changed

  • Renamed indexes to use readable {table}_{column}_idx naming convention, replacing the old truncated hash-based names. Includes a migration with RenameIndex operations (instant ALTER INDEX RENAME, no locks). (74aa8b76aa40)

Upgrade instructions

  • Run plain migrate to apply the index rename migration. This is an instant metadata-only operation with no performance impact.

0.46.4 (2026-03-25)

What's changed

  • Removed duplicate job_class indexes from JobRequest and JobProcess — these single-column indexes were redundant with existing composite indexes that share the same leading column. Adds migration to drop both (262ad569d5df)

Upgrade instructions

  • Run uv run plain postgres migrate to apply the migration.

0.46.3 (2026-03-20)

What's changed

  • Cgroup-aware worker process count — when max_processes is not explicitly set, the jobs worker now uses get_cpu_count() which respects cgroup v2 CPU quotas, preventing over-provisioning of worker processes in containerized environments (aa0e57b7eb85)
  • Structured logging — all log messages now use structured extra={} for variable data instead of inline string formatting, making logs easier to parse and filter in log aggregation tools (75a8b60c91)

Upgrade instructions

  • No changes required.

0.46.2 (2026-03-19)

What's changed

  • Fix admin job request list ordering — the admin list view now correctly orders by -priority (descending) so higher priority jobs appear first, matching the worker's processing order (b77789f0fe68)

Upgrade instructions

  • No changes required.

0.46.1 (2026-03-19)

What's changed

  • Trend card on job results — results list now shows a trend chart grouped by status with semantic colors (green/red/amber/blue/stone) (083ec33a4ec9)
  • Styled status badges — job result status is displayed as a colored badge in the list view (083ec33a4ec9)
  • Fix job priority orderingJobRequest default ordering and worker query now use -priority (descending) so higher priority values are processed first, matching documented semantics (11253fbe1c3f)

Upgrade instructions

  • Job priority ordering has changed: higher numeric priority values are now processed first (descending order). If you relied on the previous ascending order, adjust your priority values accordingly.

0.46.0 (2026-03-12)

What's changed

  • Updated all imports from plain.models to plain.postgres in admin, jobs, locks, models, parameters, workers, and migrations.
  • Updated pyproject.toml dependency from plain.models to plain.postgres.

Upgrade instructions

  • Update imports: from plain.models to from plain.postgres, from plain import models to from plain import postgres.
  • Update dependency declarations: plain.models to plain.postgres in pyproject.toml.

0.45.4 (2026-03-10)

What's changed

  • Adopted PEP 695 type parameter syntax for register_job (aa5b2db6e8ed)

Upgrade instructions

  • No changes required.

0.45.3 (2026-03-09)

What's changed

  • Updated advisory lock code to use get_connection() instead of the removed db_connection proxy (4a79279d01dd)

Upgrade instructions

  • No changes required.

0.45.2 (2026-02-26)

What's changed

  • Removed redundant allow_global_search = False from job result admin view — this is now the default in plain-admin (05d6fa2764)

Upgrade instructions

  • No changes required.

0.45.1 (2026-02-26)

What's changed

  • Auto-formatted config files with updated linter configuration (028bb95c3ae3)

Upgrade instructions

  • No changes required.

0.45.0 (2026-02-24)

What's changed

  • Worker CLI options (--max-processes, --max-jobs-per-process, --max-pending-per-process, --stats-every) now use SettingOption to read defaults from Plain settings instead of environment variables (cb5353b9d266)
  • Added JOBS_WORKER_MAX_PROCESSES, JOBS_WORKER_MAX_JOBS_PER_PROCESS, JOBS_WORKER_MAX_PENDING_PER_PROCESS, and JOBS_WORKER_STATS_EVERY settings as the new source of truth for worker defaults (cb5353b9d266)

Upgrade instructions

  • If you configured worker options via environment variables (PLAIN_JOBS_WORKER_*), move them to Plain settings (e.g., JOBS_WORKER_MAX_PROCESSES = 4 in your settings module). CLI flags still take priority when passed explicitly.

0.44.0 (2026-02-16)

What's changed

  • Removed database vendor check from get_enqueue_lock() — advisory locks are now always used since PostgreSQL is the only supported database (6f3a066bf80f)
  • Simplified README race condition documentation to reflect PostgreSQL-only support (6f3a066bf80f)

Upgrade instructions

  • No changes required.

0.43.5 (2026-02-13)

What's changed

  • Added Idempotency section to README with guidance and code examples for designing retry-safe jobs (8c2189a896d2)
  • Added scoped agent rules file with best practices for background jobs (idempotency, offloading slow work, concurrency keys) (8c2189a896d2)

Upgrade instructions

  • No changes required.

0.43.4 (2026-02-04)

What's changed

  • Added __all__ exports to models and scheduling modules for explicit public API boundaries (e7164d3891b2)

Upgrade instructions

  • No changes required.

0.43.3 (2026-01-28)

What's changed

  • Updated admin views to use the new filter_queryset hook for filtering by job result status instead of doing it inside get_initial_queryset (99d6f042b8)
  • Renamed self.preset to self.filter in admin views (99d6f042b8)
  • Added Settings section to README (803fee1ad5)

Upgrade instructions

  • No changes required.

0.43.2 (2026-01-22)

What's changed

  • Fixed JobProcess.defer() to use self.id instead of the deprecated self.pk attribute when restoring the job process ID after a failed re-enqueue attempt. This aligns with the change made in v0.25.0 where the pk alias was removed. (08863e0fb0)

Upgrade instructions

  • No changes required.

0.43.1 (2026-01-22)

What's changed

  • Fixed handling of DeferError when a job re-enqueue fails due to concurrency limits. Previously, if a deferred job couldn't be re-enqueued because the concurrency limit was already reached, the transaction would roll back but leave the JobProcess in an inconsistent state. Now the job is properly marked as errored with a descriptive error message, and the pk is restored so the job can be found again. (10ecfc6dd7)

Upgrade instructions

  • No changes required.

0.43.0 (2026-01-15)

What's changed

  • Improved admin interface with updated icons and descriptions for job request, process, and result list views (0fc4dd345f)

Upgrade instructions

  • No changes required

0.42.0 (2026-01-13)

What's changed

  • Updated to use RedirectResponse instead of ResponseRedirect to align with the core response class naming convention (fad5bf28b0)

Upgrade instructions

  • No changes required

0.41.2 (2025-12-22)

What's changed

Upgrade instructions

  • No changes required

0.41.1 (2025-11-17)

What's changed

  • Model query attributes no longer use ClassVar type annotation, reverting to simpler direct type annotations (1c624ff29e)

Upgrade instructions

  • No changes required

0.41.0 (2025-11-13)

What's changed

  • Model query attributes now use ClassVar type annotation to properly indicate they are class-level attributes that return QuerySets (c3b00a693c)

Upgrade instructions

  • No changes required

0.40.0 (2025-11-13)

What's changed

  • Added explicit type stubs for all model fields using plain.models.types for better IDE support and type checking (c8f40fc75a)
  • Model field definitions now use type-annotated syntax (e.g., field_name: type = types.CharField()) instead of plain field assignments (c8f40fc75a)
  • Fixed potential issue where parameters could be None when loading a job by providing empty dict as default (c8f40fc75a)

Upgrade instructions

  • No changes required

0.39.0 (2025-11-12)

What's changed

  • Improved type annotations across the codebase for better IDE support and type checking (f4dbcefa92)
  • Added type hints for _init_args and _init_kwargs attributes on the Job class that are set by the JobType metaclass (f4dbcefa92)
  • Fixed type annotation for get_initial_queryset() in admin views to properly indicate it returns JobResultQuerySet (f4dbcefa92)

Upgrade instructions

  • No changes required

0.38.1 (2025-11-11)

What's changed

  • Updated imports to use explicit plain.models.expressions instead of accessing Case, When, and F through plain.models namespace (e9edf61c6b)

Upgrade instructions

  • No changes required

0.38.0 (2025-11-09)

What's changed

  • Renamed unique_key to concurrency_key throughout the API for better clarity about its purpose as a grouping identifier (01b6986d79)
  • Added should_enqueue() hook for implementing custom concurrency limits and rate limiting (01b6986d79)
  • Added PostgreSQL advisory lock support to prevent race conditions when checking concurrency limits (01b6986d79)
  • Added DeferJob exception for signaling jobs should be re-tried later without counting as errors (01b6986d79)
  • Added helper methods get_requested_jobs() and get_processing_jobs() for querying jobs by concurrency key (01b6986d79)
  • Renamed job configuration methods from get_*() to default_*() (e.g., get_queue()default_queue()) to better indicate they provide defaults that can be overridden (01b6986d79)
  • Renamed get_retry_delay() to calculate_retry_delay() for better semantic clarity (01b6986d79)
  • Changed field types for priority, retries, and retry_attempt from IntegerField to SmallIntegerField for better database efficiency (01b6986d79)
  • Added DEFERRED status to job results for jobs that were deferred and will be retried (01b6986d79)

Upgrade instructions

  • Rename unique_key parameter to concurrency_key in all run_in_worker() calls
  • Rename job method get_unique_key() to default_concurrency_key() if you've overridden it
  • Rename job methods: get_queue()default_queue(), get_priority()default_priority(), get_retries()default_retries()
  • Rename get_retry_delay() to calculate_retry_delay() if you've overridden it

0.37.6 (2025-11-04)

What's changed

  • Removed info-level logging when a duplicate job is detected with a unique key to reduce log noise (b6ad845180)

Upgrade instructions

  • No changes required

0.37.5 (2025-11-04)

What's changed

  • Added info-level logging when a duplicate job is detected with a unique key, making it easier to debug and monitor job deduplication (8a9253bc63)

Upgrade instructions

  • No changes required

0.37.4 (2025-11-03)

What's changed

  • Fixed migration documentation to reference correct renamed commands: plain db shell instead of plain models db-shell and plain migrations prune instead of plain migrate --prune (b293750f6f)

Upgrade instructions

  • No changes required

0.37.3 (2025-11-03)

What's changed

  • No functional changes in this release

Upgrade instructions

  • No changes required

0.37.2 (2025-10-31)

What's changed

  • Added BSD-3-Clause license specification to package metadata (8477355e65)

Upgrade instructions

  • No changes required

0.37.1 (2025-10-24)

What's changed

  • Fixed admin interface filter functionality to correctly use preset instead of display for filtering job results (26fde7d562)

Upgrade instructions

  • No changes required

0.37.0 (2025-10-22)

What's changed

  • Added JobMiddleware abstract base class for creating custom job middleware (29e5c6df1a)
  • Changed "preparing to execute job" log message from logger.info to logger.debug to reduce log noise (8e25856639)

Upgrade instructions

  • If you have custom job middleware, update them to inherit from JobMiddleware and implement the process_job() method instead of __call__()

0.36.3 (2025-10-20)

What's changed

  • Added garbage collection back to worker processes after job completion to help manage memory usage (aafe3ace02)

Upgrade instructions

  • No changes required

0.36.2 (2025-10-20)

What's changed

  • Fixed scheduled job detection logic to properly check for None instead of checking for list type when determining if a duplicate job was scheduled (09e45fd96b)

Upgrade instructions

  • No changes required

0.36.1 (2025-10-20)

What's changed

  • Fixed run_in_worker() to properly return None when a duplicate job is detected with a unique key, instead of returning the list of in-progress jobs (5d7df365d6)

Upgrade instructions

  • No changes required

0.36.0 (2025-10-17)

What's changed

  • Removed internal memory optimization attempts including manual garbage collection and object deletion in worker processes (c7064ba329)
  • Increased worker sleep interval from 0.1s to 0.5s when no jobs are available, reducing CPU usage during idle periods (c7064ba329)

Upgrade instructions

  • No changes required

0.35.1 (2025-10-17)

What's changed

  • The run_in_worker() method now returns None when a duplicate job is detected instead of attempting to return the list of in-progress jobs (72f48d21bc)
  • Fixed type annotations for run_in_worker() to properly indicate it can return JobRequest | None (72f48d21bc)
  • The retry_job() method now properly handles explicit delay=0 parameter to intentionally retry immediately (72f48d21bc)
  • Fixed type annotations for retry_job() to properly indicate it can return JobRequest | None (72f48d21bc)

Upgrade instructions

  • No changes required

0.35.0 (2025-10-17)

What's changed

  • The Job base class is now an abstract base class requiring implementation of the run() method (e34282bba8)
  • Job worker processes now properly initialize the Plain framework before processing jobs, fixing potential startup issues (c4551d1b84)
  • The plain jobs list command now displays job descriptions from docstrings in a cleaner format (4b6881a49e)
  • Job requests in the admin interface are now ordered by priority, start time, and created time to match worker processing order (c18f0e3fb6)
  • The ClearCompleted chore has been refactored to use the new abstract base class pattern (c4466d3c60)

Upgrade instructions

  • No changes required

0.34.0 (2025-10-13)

What's changed

  • Added --reload flag to plain jobs worker command for automatic reloading when code changes are detected (f3db87e9aa)
  • Worker reloader now only watches .py and .env* files, not HTML files (f2f31c288b)

Upgrade instructions

  • Custom autoreloaders for development are no longer needed -- use the built-in --reload flag instead

0.33.0 (2025-10-10)

What's changed

  • Renamed package from plain.worker to plain.jobs (24219856e0)

Upgrade instructions

  • Update any imports from plain.worker to plain.jobs (e.g., from plain.worker import Job becomes from plain.jobs import Job)
  • Change worker commands from plain worker run to plain jobs worker
  • Check updated settings names

0.32.0 (2025-10-07)

What's changed

  • Models now use model_options instead of _meta for accessing model configuration like package_label and model_name (73ba469)
  • Model configuration now uses model_options = models.Options() instead of class Meta (17a378d)
  • QuerySet types now properly use Self return type for better type checking (2578301)
  • Removed unnecessary type ignore comments now that QuerySet is properly typed (2578301)

Upgrade instructions

  • No changes required

0.31.1 (2025-10-06)

What's changed

  • Updated dependency resolution to use newer compatible versions of plain and plain.models

Upgrade instructions

  • No changes required

0.31.0 (2025-09-25)

What's changed

  • The jobs autodiscovery now includes app.jobs modules in addition to package jobs modules (b0b610d)

Upgrade instructions

  • No changes required

0.30.0 (2025-09-19)

What's changed

  • The Job model has been renamed to JobProcess for better clarity (986c914)
  • The job_uuid field in JobResult has been renamed to job_process_uuid to match the model rename (986c914)
  • Admin interface now shows "Job processes" as the section title instead of "Jobs" (986c914)

Upgrade instructions

  • Run plain migrate to apply the database migration that renames the Job model to JobProcess
  • If you have any custom code that directly references the Job model (different than the Job base class for job type definitions), update it to use JobProcess instead
  • If you have any code that accesses the job_uuid field on JobResult instances, update it to use job_process_uuid

0.29.0 (2025-09-12)

What's changed

  • Model managers have been renamed from .objects to .query (037a239)
  • Manager functionality has been merged into QuerySet classes (bbaee93)
  • Models now use Meta.queryset_class instead of separate manager configuration (6b60a00)

Upgrade instructions

  • Update all model queries to use .query instead of .objects (e.g., Job.query.all() becomes Job.query.all())

0.28.1 (2025-09-10)

What's changed

  • Fixed log context method in worker middleware to use include_context instead of with_context (755f873)

Upgrade instructions

  • No changes required

0.28.0 (2025-09-09)

What's changed

  • Improved logging middleware to use context manager pattern for cleaner job context handling (ea7c953)
  • Updated minimum Python requirement to 3.13 (d86e307)
  • Added explicit nav_icon definitions to admin views to ensure consistent icon display (2aac07d)

Upgrade instructions

  • No changes required

0.27.1 (2025-08-27)

What's changed

  • Jobs are now marked as cancelled when the worker process is killed or fails unexpectedly (e73ca53)

Upgrade instructions

  • No changes required

0.27.0 (2025-08-22)

What's changed

  • Added support for date and datetime job parameters with proper serialization/deserialization (7bb5ab0911)
  • Improved job priority documentation to clarify that higher numbers run first (73271b5bf0)
  • Updated admin interface with consolidated navigation icons at the section level (5a6479ac79)
  • Enhanced admin views to use cached object properties for better performance (bd0507a72c)

Upgrade instructions

  • No changes required

0.26.0 (2025-08-19)

What's changed

  • Improved CSRF token handling in admin forms by removing manual csrf_input in favor of automatic Sec-Fetch-Site header validation (955150800c)
  • Enhanced README documentation with comprehensive examples, table of contents, and detailed sections covering job parameters, scheduling, monitoring, and FAQs (4ebecd1856)
  • Updated package description to be more descriptive: "Process background jobs with a database-driven worker" (4ebecd1856)

Upgrade instructions

  • No changes required

0.25.1 (2025-07-23)

What's changed

  • Added Bootstrap icons to admin interface for worker job views (9e9f8b0)
  • Removed the description field from admin views (8d2352d)

Upgrade instructions

  • No changes required

0.25.0 (2025-07-22)

What's changed

  • Removed pk alias and auto fields in favor of a single automatic id PrimaryKeyField (4b8fa6a)
  • Admin interface methods now use target_ids parameter instead of target_pks for batch actions
  • Model instance registry now uses .id instead of .pk for Global ID generation
  • Updated database migrations to use models.PrimaryKeyField() instead of models.BigAutoField()

Upgrade instructions

  • No changes required

0.24.0 (2025-07-18)

What's changed

  • Added OpenTelemetry tracing support to job processing system (b0224d0)
  • Job requests now capture trace context when queued from traced operations
  • Job execution creates proper consumer spans linked to the original producer trace
  • Added trace_id and span_id fields to JobRequest, Job, and JobResult models for trace correlation

Upgrade instructions

  • Run plain migrate to apply new database migration that adds trace context fields to worker tables

0.23.0 (2025-07-18)

What's changed

  • Migrations have been reset and consolidated into a single initial migration (484f1b6e93)

Upgrade instructions

  • Run plain migrate --prune plainworker to remove old migration records and apply the consolidated migration

0.22.5 (2025-06-24)

What's changed

  • No functional changes. This release only updates internal documentation (CHANGELOG) and contains no code modifications that impact users (82710c3, 9a1963d, e1f5dd3).

Upgrade instructions

  • No changes required