From f294cd177530787a1f1e19aa90ee733583be1397 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 29 Sep 2025 15:16:19 -0400 Subject: [PATCH 1/2] Fixes #20243: Prevent scheduled system jobs from re-running multiple times --- netbox/netbox/jobs.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/netbox/netbox/jobs.py b/netbox/netbox/jobs.py index 559619ac082..60dc688b95a 100644 --- a/netbox/netbox/jobs.py +++ b/netbox/netbox/jobs.py @@ -4,6 +4,7 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.functional import classproperty +from django.utils import timezone from django_pglocks import advisory_lock from rq.timeouts import JobTimeoutException @@ -113,7 +114,11 @@ def handle(cls, job, *args, **kwargs): # If the executed job is a periodic job, schedule its next execution at the specified interval. finally: if job.interval: - new_scheduled_time = (job.scheduled or job.started) + timedelta(minutes=job.interval) + # Determine the new scheduled time. Cannot be earlier than one minute in the future. + new_scheduled_time = max([ + (job.scheduled or job.started) + timedelta(minutes=job.interval), + timezone.now() + timedelta(minutes=1) + ]) if job.object and getattr(job.object, "python_class", None): kwargs["job_timeout"] = job.object.python_class.job_timeout cls.enqueue( From 512d712d3df0f3279910613cc5567569bf17877e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 30 Sep 2025 14:11:06 -0400 Subject: [PATCH 2/2] Remove extraneous list encapsulation --- netbox/netbox/jobs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/netbox/jobs.py b/netbox/netbox/jobs.py index 60dc688b95a..72dcece1f22 100644 --- a/netbox/netbox/jobs.py +++ b/netbox/netbox/jobs.py @@ -115,10 +115,10 @@ def handle(cls, job, *args, **kwargs): finally: if job.interval: # Determine the new scheduled time. Cannot be earlier than one minute in the future. - new_scheduled_time = max([ + new_scheduled_time = max( (job.scheduled or job.started) + timedelta(minutes=job.interval), timezone.now() + timedelta(minutes=1) - ]) + ) if job.object and getattr(job.object, "python_class", None): kwargs["job_timeout"] = job.object.python_class.job_timeout cls.enqueue(