plain.jobs 0.35.0__tar.gz → 0.36.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of plain.jobs might be problematic. Click here for more details.

Files changed (31) hide show
  1. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/PKG-INFO +1 -1
  2. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/CHANGELOG.md +24 -0
  3. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/jobs.py +2 -3
  4. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/models.py +11 -10
  5. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/workers.py +1 -16
  6. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/pyproject.toml +1 -1
  7. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/.gitignore +0 -0
  8. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/LICENSE +0 -0
  9. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/README.md +0 -0
  10. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/README.md +0 -0
  11. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/__init__.py +0 -0
  12. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/admin.py +0 -0
  13. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/chores.py +0 -0
  14. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/cli.py +0 -0
  15. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/config.py +0 -0
  16. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/default_settings.py +0 -0
  17. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/middleware.py +0 -0
  18. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/0001_initial.py +0 -0
  19. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/0002_job_span_id_job_trace_id_jobrequest_span_id_and_more.py +0 -0
  20. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/0003_rename_job_jobprocess_and_more.py +0 -0
  21. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/0004_rename_tables_to_plainjobs.py +0 -0
  22. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/0005_rename_constraints_and_indexes.py +0 -0
  23. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/0006_alter_jobprocess_table_alter_jobrequest_table_and_more.py +0 -0
  24. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/migrations/__init__.py +0 -0
  25. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/parameters.py +0 -0
  26. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/registry.py +0 -0
  27. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/scheduling.py +0 -0
  28. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/plain/jobs/templates/admin/plainqueue/jobresult_detail.html +0 -0
  29. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/tests/app/settings.py +0 -0
  30. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/tests/test_parameters.py +0 -0
  31. {plain_jobs-0.35.0 → plain_jobs-0.36.0}/tests/test_scheduling.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.jobs
3
- Version: 0.35.0
3
+ Version: 0.36.0
4
4
  Summary: Process background jobs with a database-driven job queue.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-File: LICENSE
@@ -1,5 +1,29 @@
1
1
  # plain-jobs changelog
2
2
 
3
+ ## [0.36.0](https://github.com/dropseed/plain/releases/plain-jobs@0.36.0) (2025-10-17)
4
+
5
+ ### What's changed
6
+
7
+ - Removed internal memory optimization attempts including manual garbage collection and object deletion in worker processes ([c7064ba329](https://github.com/dropseed/plain/commit/c7064ba329))
8
+ - Increased worker sleep interval from 0.1s to 0.5s when no jobs are available, reducing CPU usage during idle periods ([c7064ba329](https://github.com/dropseed/plain/commit/c7064ba329))
9
+
10
+ ### Upgrade instructions
11
+
12
+ - No changes required
13
+
14
+ ## [0.35.1](https://github.com/dropseed/plain/releases/plain-jobs@0.35.1) (2025-10-17)
15
+
16
+ ### What's changed
17
+
18
+ - 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](https://github.com/dropseed/plain/commit/72f48d21bc))
19
+ - Fixed type annotations for `run_in_worker()` to properly indicate it can return `JobRequest | None` ([72f48d21bc](https://github.com/dropseed/plain/commit/72f48d21bc))
20
+ - The `retry_job()` method now properly handles explicit `delay=0` parameter to intentionally retry immediately ([72f48d21bc](https://github.com/dropseed/plain/commit/72f48d21bc))
21
+ - Fixed type annotations for `retry_job()` to properly indicate it can return `JobRequest | None` ([72f48d21bc](https://github.com/dropseed/plain/commit/72f48d21bc))
22
+
23
+ ### Upgrade instructions
24
+
25
+ - No changes required
26
+
3
27
  ## [0.35.0](https://github.com/dropseed/plain/releases/plain-jobs@0.35.0) (2025-10-17)
4
28
 
5
29
  ### What's changed
@@ -62,7 +62,7 @@ class Job(metaclass=JobType):
62
62
  retries: int | None = None,
63
63
  retry_attempt: int = 0,
64
64
  unique_key: str | None = None,
65
- ) -> JobRequest | list[JobRequest | JobProcess]:
65
+ ) -> JobRequest | None:
66
66
  from .models import JobRequest
67
67
 
68
68
  job_class_name = jobs_registry.get_job_class_name(self.__class__)
@@ -170,8 +170,7 @@ class Job(metaclass=JobType):
170
170
  span.set_attribute(ERROR_TYPE, "IntegrityError")
171
171
  span.set_status(trace.Status(trace.StatusCode.ERROR, "Duplicate job"))
172
172
  logger.warning("Job already in progress: %s", e)
173
- # Try to return the _in_progress list again
174
- return self._in_progress(unique_key)
173
+ return None
175
174
 
176
175
  def _in_progress(self, unique_key: str) -> list[JobRequest | JobProcess]:
177
176
  """Get all JobRequests and JobProcess that are currently in progress, regardless of queue."""
@@ -412,10 +412,14 @@ class JobResult(models.Model):
412
412
  ],
413
413
  )
414
414
 
415
- def retry_job(self, delay: int | None = None) -> JobRequest:
415
+ def retry_job(self, delay: int | None = None) -> JobRequest | None:
416
416
  retry_attempt = self.retry_attempt + 1
417
417
  job = jobs_registry.load_job(self.job_class, self.parameters)
418
- retry_delay = delay or job.get_retry_delay(retry_attempt)
418
+
419
+ if delay is None:
420
+ retry_delay = job.get_retry_delay(retry_attempt)
421
+ else:
422
+ retry_delay = delay
419
423
 
420
424
  with transaction.atomic():
421
425
  result = job.run_in_worker(
@@ -427,12 +431,9 @@ class JobResult(models.Model):
427
431
  retry_attempt=retry_attempt,
428
432
  # Unique key could be passed also?
429
433
  )
434
+ if result:
435
+ self.retry_job_request_uuid = result.uuid
436
+ self.save(update_fields=["retry_job_request_uuid"])
437
+ return result
430
438
 
431
- # TODO it is actually possible that result is a list
432
- # of pending jobs, which would need to be handled...
433
- # Right now it will throw an exception which could be caught by retry_failed_jobs.
434
-
435
- self.retry_job_request_uuid = result.uuid # type: ignore
436
- self.save(update_fields=["retry_job_request_uuid"])
437
-
438
- return result # type: ignore
439
+ return None
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import gc
4
3
  import logging
5
4
  import multiprocessing
6
5
  import os
@@ -122,7 +121,7 @@ class Worker:
122
121
  # We don't want to convert too many JobRequests to Jobs,
123
122
  # because anything not started yet will be cancelled on deploy etc.
124
123
  # It's easier to leave them in the JobRequest db queue as long as possible.
125
- time.sleep(0.1)
124
+ time.sleep(0.5)
126
125
  continue
127
126
 
128
127
  with transaction.atomic():
@@ -157,19 +156,11 @@ class Worker:
157
156
 
158
157
  job_process_uuid = str(job.uuid) # Make a str copy
159
158
 
160
- # Release these now
161
- del job_request
162
- del job
163
-
164
159
  future = self.executor.submit(process_job, job_process_uuid)
165
160
  future.add_done_callback(
166
161
  partial(future_finished_callback, job_process_uuid)
167
162
  )
168
163
 
169
- # Do a quick sleep regardless to see if it
170
- # gives processes a chance to start up
171
- time.sleep(0.1)
172
-
173
164
  def shutdown(self) -> None:
174
165
  if self._is_shutting_down:
175
166
  # Already shutting down somewhere else
@@ -339,9 +330,6 @@ def process_job(job_process_uuid: str) -> None:
339
330
 
340
331
  job_result = middleware_chain(job_process)
341
332
 
342
- # Release it now
343
- del job_process
344
-
345
333
  duration = job_result.ended_at - job_result.started_at # type: ignore[unsupported-operator]
346
334
  duration = duration.total_seconds()
347
335
 
@@ -357,8 +345,6 @@ def process_job(job_process_uuid: str) -> None:
357
345
  job_result.queue,
358
346
  duration,
359
347
  )
360
-
361
- del job_result
362
348
  except Exception as e:
363
349
  # Raising exceptions inside the worker process doesn't
364
350
  # seem to be caught/shown anywhere as configured.
@@ -367,4 +353,3 @@ def process_job(job_process_uuid: str) -> None:
367
353
  logger.exception(e)
368
354
  finally:
369
355
  request_finished.send(sender=None)
370
- gc.collect()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain.jobs"
3
- version = "0.35.0"
3
+ version = "0.36.0"
4
4
  description = "Process background jobs with a database-driven job queue."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  readme = "README.md"
File without changes
File without changes
File without changes