Development #36215
jobs, pouvoir renseigner une date/heure minimale d'exécution
0%
Description
(parce qu'après avoir refait cron(1), autant refaire at(1) aussi)
En fait l'idée c'est pouvoir planifier un rejeu, qui ne soit pas immédiat.
Fichiers
Révisions associées
misc: prevent locking all jobs (#36215)
.first() does list(qs)[:1] which will select all target jobs,
we must add a LIMIT 1 before .first() to lock only the job we are
looking for. Ordering is necessary as .first() will do an
.order_by('pk') on an unordered queryset to get a deterministic result
in all cases and ordering a sliced queryset is not possible.
misc: add after_timestamp to run Job later (#36215)
The after_timestamp can be set:
- when adding the job with:
self.add_job(..., after_timestamp=datetime(...))
- when skipping a job with:
raise SkipJob(after_timestamp=datetime(...))
Historique
Mis à jour par Benjamin Dauvergne il y a plus de 4 ans
- Fichier 0001-misc-prevent-locking-all-jobs-36215.patch 0001-misc-prevent-locking-all-jobs-36215.patch ajouté
- Fichier 0002-misc-add-after_timestamp-to-run-Job-later-36215.patch 0002-misc-add-after_timestamp-to-run-Job-later-36215.patch ajouté
- Statut changé de Nouveau à Solution proposée
- Patch proposed changé de Non à Oui
Mis à jour par Frédéric Péters il y a plus de 4 ans
Est-ce que le after_timestamp pourrait aussi accepter un timedelta (qui se ferait par rapport à .now()) ?
Mis à jour par Benjamin Dauvergne il y a plus de 4 ans
- Fichier 0001-misc-prevent-locking-all-jobs-36215.patch 0001-misc-prevent-locking-all-jobs-36215.patch ajouté
- Fichier 0002-misc-add-after_timestamp-to-run-Job-later-36215.patch 0002-misc-add-after_timestamp-to-run-Job-later-36215.patch ajouté
Interdiff:
diff --git a/passerelle/base/models.py b/passerelle/base/models.py index e38efe3b..d866addf 100644 --- a/passerelle/base/models.py +++ b/passerelle/base/models.py @@ -523,7 +523,7 @@ class BaseResource(models.Model): getattr(self, job.method_name)(**job.parameters) except SkipJob as e: job.status = 'registered' - job.after_timestamp = e.after_timestamp + job.set_after_timestamp(e.after_timestamp) skipped_jobs.append(job.id) except Exception as e: self.handle_job_error(job, sys.exc_info()) @@ -538,8 +538,8 @@ class BaseResource(models.Model): resource_pk=self.pk, method_name=method_name, natural_id=natural_id, - after_timestamp=after_timestamp, parameters=kwargs) + job.set_after_timestamp(after_timestamp) job.save() return job @@ -679,6 +679,16 @@ class Job(models.Model): ) status_details = jsonfield.JSONField(default={}) + def set_after_timestamp(self, value): + if isinstance(value, datetime.datetime): + self.after_timestamp = value + elif isinstance(value, six.integer_types + (float,)): + self.after_timestamp = timezone.now() + datetime.timedelta(seconds=value) + elif isinstance(value, datetime.timedelta): + self.after_timestamp = timezone.now() + value + else: + self.after_timestamp = value + class ResourceLog(models.Model): timestamp = models.DateTimeField(auto_now_add=True) diff --git a/tests/test_jobs.py b/tests/test_jobs.py index e932909f..c79d6413 100644 --- a/tests/test_jobs.py +++ b/tests/test_jobs.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import datetime import os import mock @@ -40,15 +41,46 @@ def test_jobs(mocked_get, app, base_adresse, freezer): assert Job.objects.get(id=job.id).status == 'registered' # use after_timestamp with SkipJob - mocked_get.side_effect = None freezer.move_to('2019-01-01 00:00:00') - Job.objects.filter(id=job.id).update(after_timestamp='2019-01-02 00:00:00') + mocked_get.side_effect = SkipJob(after_timestamp='2019-01-02 00:00:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + mocked_get.side_effect = None + freezer.move_to('2019-01-01 12:00:00') base_adresse.jobs() assert Job.objects.get(id=job.id).status == 'registered' freezer.move_to('2019-01-02 01:00:00') base_adresse.jobs() assert Job.objects.get(id=job.id).status == 'completed' + # use after_timestamp with SkipJob and seconds + job = base_adresse.add_job('update_streets_data') + freezer.move_to('2019-01-01 00:00:00') + mocked_get.side_effect = SkipJob(after_timestamp=3600) + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + mocked_get.side_effect = None + freezer.move_to('2019-01-01 00:30:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + freezer.move_to('2019-01-01 01:01:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'completed' + + # use after_timestamp with SkipJob and timedelta + job = base_adresse.add_job('update_streets_data') + freezer.move_to('2019-01-01 00:00:00') + mocked_get.side_effect = SkipJob(after_timestamp=datetime.timedelta(seconds=3600)) + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + mocked_get.side_effect = None + freezer.move_to('2019-01-01 00:30:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + freezer.move_to('2019-01-01 01:01:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'completed' + # use after_timestamp with add_job freezer.move_to('2019-01-01 00:00:00') job = base_adresse.add_job('update_streets_data', after_timestamp='2019-01-02 00:00:00') @@ -58,6 +90,24 @@ def test_jobs(mocked_get, app, base_adresse, freezer): base_adresse.jobs() assert Job.objects.get(id=job.id).status == 'completed' + # use after_timestamp with add_job and seconds + freezer.move_to('2019-01-01 00:00:00') + job = base_adresse.add_job('update_streets_data', after_timestamp=3600) + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + freezer.move_to('2019-01-01 01:01:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'completed' + + # use after_timestamp with add_job and seconds + freezer.move_to('2019-01-01 00:00:00') + job = base_adresse.add_job('update_streets_data', after_timestamp=datetime.timedelta(seconds=3600)) + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'registered' + freezer.move_to('2019-01-01 01:01:00') + base_adresse.jobs() + assert Job.objects.get(id=job.id).status == 'completed' + # don't run jobs if connector is down StreetModel.objects.all().delete() with mock.patch('passerelle.apps.base_adresse.models.BaseAdresse.down') as down:
Mis à jour par Frédéric Péters il y a plus de 4 ans
- Statut changé de Solution proposée à Solution validée
Quand même vraiment pas fan de cette pratique de réindenter de manière toute différente, et encore moins de tout taper entre parenthèses. For the record donc, à qui voudrait relire le patch dans le futur,
+ Q(after_timestamp__isnull=True) | Q(after_timestamp__lt=timezone.now()),
Anyway.
Mis à jour par Benjamin Dauvergne il y a plus de 4 ans
- Fichier 0003-misc-add-after_timestamp-to-run-Job-later-36215.patch 0003-misc-add-after_timestamp-to-run-Job-later-36215.patch ajouté
- Fichier 0001-misc-remove-unused-statement-in-jobs-36215.patch 0001-misc-remove-unused-statement-in-jobs-36215.patch ajouté
- Fichier 0002-misc-prevent-locking-all-jobs-36215.patch 0002-misc-prevent-locking-all-jobs-36215.patch ajouté
- Statut changé de Solution validée à Solution proposée
Voilà, juste que c'est pas PEP8 et juste assez illisible de ne pas commencer les lignes par un opérateur, même si les patchs minimaux c'est bien aussi.
Mis à jour par Frédéric Péters il y a plus de 4 ans
- Statut changé de Solution proposée à Solution validée
(je ne vérifie pas)
Mis à jour par Benjamin Dauvergne il y a plus de 4 ans
- Statut changé de Solution validée à Résolu (à déployer)
commit d629c50e2eccb271d9a84ac6d7120fc3ca29b645 Author: Benjamin Dauvergne <bdauvergne@entrouvert.com> Date: Sat Sep 21 11:06:39 2019 +0200 misc: add after_timestamp to run Job later (#36215) The after_timestamp can be set: - when adding the job with: self.add_job(..., after_timestamp=datetime(...)) - when skipping a job with: raise SkipJob(after_timestamp=datetime(...)) commit 95904dbaae1fd7933e2b6ec6304aeb58c14a7ffd Author: Benjamin Dauvergne <bdauvergne@entrouvert.com> Date: Sat Sep 21 10:51:21 2019 +0200 misc: prevent locking all jobs (#36215) .first() does list(qs)[:1] which will select all target jobs, we must add a LIMIT 1 before .first() to lock only the job we are looking for. Ordering is necessary as .first() will do an .order_by('pk') on an unordered queryset to get a deterministic result in all cases and ordering a sliced queryset is not possible. commit 8f39fbc38ca19a3d7740743b21845eba32048762 Author: Benjamin Dauvergne <bdauvergne@entrouvert.com> Date: Tue Oct 1 10:30:13 2019 +0200 misc: remove unused statement in jobs() (#36215) Should have been removed in a39595b.
Mis à jour par Frédéric Péters il y a plus de 4 ans
- Statut changé de Résolu (à déployer) à Solution déployée
misc: remove unused statement in jobs() (#36215)
Should have been removed in a39595b.