v0.146.0
Release plain 0.146.0
bbb1486
·
10h ago
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
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 INTERNAL → CONSUMER 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
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
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
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
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
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.filepath → code.file.path, code.lineno → code.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
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
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
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 ordering —
JobRequest 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
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
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
0.45.1 (2026-02-26)
What's changed
- Auto-formatted config files with updated linter configuration (028bb95c3ae3)
Upgrade instructions
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
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
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
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
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
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
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
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
0.41.2 (2025-12-22)
What's changed
Upgrade instructions
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
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
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
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
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
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
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
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
0.37.3 (2025-11-03)
What's changed
- No functional changes in this release
Upgrade instructions
0.37.2 (2025-10-31)
What's changed
- Added BSD-3-Clause license specification to package metadata (8477355e65)
Upgrade instructions
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
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
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
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
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
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
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
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
0.31.1 (2025-10-06)
What's changed
- Updated dependency resolution to use newer compatible versions of
plain and plain.models
Upgrade instructions
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
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
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
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
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
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
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
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
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