diff --git a/README.md b/README.md
index bcca4cd784..820c3ddba0 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Available addons
----------------
addon | version | maintainers | summary
--- | --- | --- | ---
-[queue_job](queue_job/) | 17.0.1.1.1 | [](https://github.com/guewen) | Job Queue
+[queue_job](queue_job/) | 17.0.1.3.0 | [](https://github.com/guewen) | Job Queue
[queue_job_cron](queue_job_cron/) | 17.0.1.0.0 | | Scheduled Actions as Queue Jobs
[queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 17.0.1.0.0 | [](https://github.com/ivantodorovich) | Run jobs without a dedicated JobRunner
[queue_job_subscribe](queue_job_subscribe/) | 17.0.1.0.0 | | Control which users are subscribed to queue job notifications
diff --git a/queue_job/README.rst b/queue_job/README.rst
index 50d20da798..6bb4747edf 100644
--- a/queue_job/README.rst
+++ b/queue_job/README.rst
@@ -7,7 +7,7 @@ Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:985afa6fddcad78278ffe6f760e0483e547b2dc57dad1d829187d485ac1a22cb
+ !! source digest: sha256:56ab2288bc8332ef24cb27d98f8851066a87bd7a84c314094a99edef659d608a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
@@ -158,6 +158,10 @@ Configuration
- Tip: to enable debug logging for the queue job, use
``--log-handler=odoo.addons.queue_job:DEBUG``
+- Jobs that remain in ``enqueued`` or ``started`` state (because, for
+ instance, their worker has been killed) will be automatically
+ re-queued.
+
.. [1]
It works with the threaded Odoo server too, although this way of
running Odoo is obviously not for production purposes.
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index 93ae82789c..5e67869b41 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -2,7 +2,7 @@
{
"name": "Job Queue",
- "version": "17.0.1.1.1",
+ "version": "17.0.1.3.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index f18401476f..ca3e02acaa 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -31,6 +31,8 @@ def _try_perform_job(self, env, job):
job.set_started()
job.store()
env.cr.commit()
+ job.lock()
+
_logger.debug("%s started", job)
job.perform()
diff --git a/queue_job/data/queue_data.xml b/queue_job/data/queue_data.xml
index ca5a747746..a2680cc475 100644
--- a/queue_job/data/queue_data.xml
+++ b/queue_job/data/queue_data.xml
@@ -1,15 +1,6 @@
-
- Jobs Garbage Collector
- 5
- minutes
- -1
-
- code
- model.requeue_stuck_jobs()
-
Job failed
diff --git a/queue_job/i18n/de.po b/queue_job/i18n/de.po
index c56f8453ce..db0b269e36 100644
--- a/queue_job/i18n/de.po
+++ b/queue_job/i18n/de.po
@@ -169,6 +169,7 @@ msgstr "Erstellt am"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_uid
@@ -177,6 +178,7 @@ msgstr "Erstellt von"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_date
@@ -223,6 +225,7 @@ msgstr "Beschreibung"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__display_name
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__display_name
@@ -353,6 +356,7 @@ msgstr ""
#: model:ir.model.fields,field_description:queue_job.field_queue_job__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__id
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__id
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__id
@@ -374,13 +378,6 @@ msgstr "Dies ist das Icon zur Kennzeichnung eines Aktivitätsfehlers."
msgid "Identity Key"
msgstr "Identitätsschlüssel"
-#. module: queue_job
-#. odoo-python
-#: code:addons/queue_job/models/queue_job.py:0
-#, python-format
-msgid "If both parameters are 0, ALL jobs will be requeued!"
-msgstr "Wenn beide Parameter 0 sind, werden ALLE Jobs neu eingereiht!"
-
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction
msgid "If checked, new messages require your attention."
@@ -475,11 +472,6 @@ msgstr "Job unterbrochen und als Erledigt markiert: Es ist nicht zu tun."
msgid "Jobs"
msgstr "Jobs"
-#. module: queue_job
-#: model:ir.actions.server,name:queue_job.ir_cron_queue_job_garbage_collector_ir_actions_server
-msgid "Jobs Garbage Collector"
-msgstr ""
-
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -494,6 +486,7 @@ msgstr "Kwargs"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_uid
@@ -502,6 +495,7 @@ msgstr "Zuletzt aktualisiert von"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_date
@@ -656,9 +650,15 @@ msgstr "Warteschlange"
#. module: queue_job
#: model:ir.model,name:queue_job.model_queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__queue_job_id
msgid "Queue Job"
msgstr "Job einreihen"
+#. module: queue_job
+#: model:ir.model,name:queue_job.model_queue_job_lock
+msgid "Queue Job Lock"
+msgstr ""
+
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -932,6 +932,10 @@ msgstr "Assistent zur erneuten Einreihung einer Job-Auswahl"
msgid "Worker Pid"
msgstr ""
+#, python-format
+#~ msgid "If both parameters are 0, ALL jobs will be requeued!"
+#~ msgstr "Wenn beide Parameter 0 sind, werden ALLE Jobs neu eingereiht!"
+
#~ msgid "SMS Delivery error"
#~ msgstr "Fehler bei der SMS Nachrichtenübermittlung"
diff --git a/queue_job/i18n/es.po b/queue_job/i18n/es.po
index 530de6dad9..aaa626a4d8 100644
--- a/queue_job/i18n/es.po
+++ b/queue_job/i18n/es.po
@@ -172,6 +172,7 @@ msgstr "Fecha de creación"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_uid
@@ -180,6 +181,7 @@ msgstr "Creado por"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_date
@@ -226,6 +228,7 @@ msgstr "Descripción"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__display_name
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__display_name
@@ -356,6 +359,7 @@ msgstr "Tiene un mensaje"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__id
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__id
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__id
@@ -377,15 +381,6 @@ msgstr "Icono para indicar una actividad de excepción."
msgid "Identity Key"
msgstr "Clave identificadora"
-#. module: queue_job
-#. odoo-python
-#: code:addons/queue_job/models/queue_job.py:0
-#, python-format
-msgid "If both parameters are 0, ALL jobs will be requeued!"
-msgstr ""
-"Si ambos parámetros son 0, ¡TODOS los trabajos se volverán a poner en la "
-"cola!"
-
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction
msgid "If checked, new messages require your attention."
@@ -478,11 +473,6 @@ msgstr "Trabajo interrumpido y marcado como hecho: nada que hacer."
msgid "Jobs"
msgstr "Trabajos"
-#. module: queue_job
-#: model:ir.actions.server,name:queue_job.ir_cron_queue_job_garbage_collector_ir_actions_server
-msgid "Jobs Garbage Collector"
-msgstr "Recolector de basura de trabajos"
-
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -497,6 +487,7 @@ msgstr "Kwargs"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_uid
@@ -505,6 +496,7 @@ msgstr "Última actualización por"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_date
@@ -666,9 +658,15 @@ msgstr "Cola"
#. module: queue_job
#: model:ir.model,name:queue_job.model_queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__queue_job_id
msgid "Queue Job"
msgstr "Cola de trabajos"
+#. module: queue_job
+#: model:ir.model,name:queue_job.model_queue_job_lock
+msgid "Queue Job Lock"
+msgstr ""
+
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -953,6 +951,15 @@ msgstr "Asistente para volver a poner en cola una selección de trabajos"
msgid "Worker Pid"
msgstr "Pid del trabajador"
+#, python-format
+#~ msgid "If both parameters are 0, ALL jobs will be requeued!"
+#~ msgstr ""
+#~ "Si ambos parámetros son 0, ¡TODOS los trabajos se volverán a poner en la "
+#~ "cola!"
+
+#~ msgid "Jobs Garbage Collector"
+#~ msgstr "Recolector de basura de trabajos"
+
#~ msgid "SMS Delivery error"
#~ msgstr "Error de entrega del SMS"
diff --git a/queue_job/i18n/it.po b/queue_job/i18n/it.po
index e98e1f06c4..3159d96e59 100644
--- a/queue_job/i18n/it.po
+++ b/queue_job/i18n/it.po
@@ -172,6 +172,7 @@ msgstr "Data creazione"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_uid
@@ -180,6 +181,7 @@ msgstr "Creato da"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_date
@@ -226,6 +228,7 @@ msgstr "Descrizione"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__display_name
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__display_name
@@ -356,6 +359,7 @@ msgstr "Ha un messaggio"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__id
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__id
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__id
@@ -377,13 +381,6 @@ msgstr "Icona per indicare un'attività eccezione."
msgid "Identity Key"
msgstr "Chiave identità"
-#. module: queue_job
-#. odoo-python
-#: code:addons/queue_job/models/queue_job.py:0
-#, python-format
-msgid "If both parameters are 0, ALL jobs will be requeued!"
-msgstr "Se entrambi i parametri sono 0, tutti i lavori verranno riaccodati!"
-
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction
msgid "If checked, new messages require your attention."
@@ -475,11 +472,6 @@ msgstr "Lavoro interrotto e impostato a completato: nulla da fare."
msgid "Jobs"
msgstr "Lavori"
-#. module: queue_job
-#: model:ir.actions.server,name:queue_job.ir_cron_queue_job_garbage_collector_ir_actions_server
-msgid "Jobs Garbage Collector"
-msgstr "Garbage collector lavori"
-
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -494,6 +486,7 @@ msgstr "Kwargs"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_uid
@@ -502,6 +495,7 @@ msgstr "Ultimo aggiornamento di"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_date
@@ -663,9 +657,15 @@ msgstr "Coda"
#. module: queue_job
#: model:ir.model,name:queue_job.model_queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__queue_job_id
msgid "Queue Job"
msgstr "Lavoro in coda"
+#. module: queue_job
+#: model:ir.model,name:queue_job.model_queue_job_lock
+msgid "Queue Job Lock"
+msgstr ""
+
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -948,5 +948,12 @@ msgstr "Procedura guidata per riaccodare una selezione di lavori"
msgid "Worker Pid"
msgstr "PID worker"
+#, python-format
+#~ msgid "If both parameters are 0, ALL jobs will be requeued!"
+#~ msgstr "Se entrambi i parametri sono 0, tutti i lavori verranno riaccodati!"
+
+#~ msgid "Jobs Garbage Collector"
+#~ msgstr "Garbage collector lavori"
+
#~ msgid "SMS Delivery error"
#~ msgstr "Errore consegna SMS"
diff --git a/queue_job/i18n/queue_job.pot b/queue_job/i18n/queue_job.pot
index 6a3515a101..8aaa602147 100644
--- a/queue_job/i18n/queue_job.pot
+++ b/queue_job/i18n/queue_job.pot
@@ -165,6 +165,7 @@ msgstr ""
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_uid
@@ -173,6 +174,7 @@ msgstr ""
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_date
@@ -219,6 +221,7 @@ msgstr ""
#: model:ir.model.fields,field_description:queue_job.field_queue_job__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__display_name
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__display_name
@@ -349,6 +352,7 @@ msgstr ""
#: model:ir.model.fields,field_description:queue_job.field_queue_job__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__id
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__id
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__id
@@ -370,13 +374,6 @@ msgstr ""
msgid "Identity Key"
msgstr ""
-#. module: queue_job
-#. odoo-python
-#: code:addons/queue_job/models/queue_job.py:0
-#, python-format
-msgid "If both parameters are 0, ALL jobs will be requeued!"
-msgstr ""
-
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction
msgid "If checked, new messages require your attention."
@@ -468,11 +465,6 @@ msgstr ""
msgid "Jobs"
msgstr ""
-#. module: queue_job
-#: model:ir.actions.server,name:queue_job.ir_cron_queue_job_garbage_collector_ir_actions_server
-msgid "Jobs Garbage Collector"
-msgstr ""
-
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -487,6 +479,7 @@ msgstr ""
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_uid
@@ -495,6 +488,7 @@ msgstr ""
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_date
@@ -646,9 +640,15 @@ msgstr ""
#. module: queue_job
#: model:ir.model,name:queue_job.model_queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__queue_job_id
msgid "Queue Job"
msgstr ""
+#. module: queue_job
+#: model:ir.model,name:queue_job.model_queue_job_lock
+msgid "Queue Job Lock"
+msgstr ""
+
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
diff --git a/queue_job/i18n/zh_CN.po b/queue_job/i18n/zh_CN.po
index f6675a5535..897aa1e4ea 100644
--- a/queue_job/i18n/zh_CN.po
+++ b/queue_job/i18n/zh_CN.po
@@ -172,6 +172,7 @@ msgstr "创建日期"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_uid
@@ -180,6 +181,7 @@ msgstr "创建者"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__create_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__create_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__create_date
@@ -226,6 +228,7 @@ msgstr "说明"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__display_name
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__display_name
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__display_name
@@ -356,6 +359,7 @@ msgstr "有消息"
#: model:ir.model.fields,field_description:queue_job.field_queue_job__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__id
#: model:ir.model.fields,field_description:queue_job.field_queue_job_function__id
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__id
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__id
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__id
@@ -377,13 +381,6 @@ msgstr "指示异常活动的图标。"
msgid "Identity Key"
msgstr "身份密钥"
-#. module: queue_job
-#. odoo-python
-#: code:addons/queue_job/models/queue_job.py:0
-#, python-format
-msgid "If both parameters are 0, ALL jobs will be requeued!"
-msgstr "如果两个参数都为0,所有任务都将被重新排队!"
-
#. module: queue_job
#: model:ir.model.fields,help:queue_job.field_queue_job__message_needaction
msgid "If checked, new messages require your attention."
@@ -475,11 +472,6 @@ msgstr "作业中断并设置为已完成:无需执行任何操作。"
msgid "Jobs"
msgstr "作业"
-#. module: queue_job
-#: model:ir.actions.server,name:queue_job.ir_cron_queue_job_garbage_collector_ir_actions_server
-msgid "Jobs Garbage Collector"
-msgstr "作业垃圾收集器"
-
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -494,6 +486,7 @@ msgstr "关键字参数"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_uid
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_uid
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_uid
@@ -502,6 +495,7 @@ msgstr "最后更新者"
#. module: queue_job
#: model:ir.model.fields,field_description:queue_job.field_queue_job_channel__write_date
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_cancelled__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_jobs_to_done__write_date
#: model:ir.model.fields,field_description:queue_job.field_queue_requeue_job__write_date
@@ -661,9 +655,15 @@ msgstr "队列"
#. module: queue_job
#: model:ir.model,name:queue_job.model_queue_job
+#: model:ir.model.fields,field_description:queue_job.field_queue_job_lock__queue_job_id
msgid "Queue Job"
msgstr "队列作业"
+#. module: queue_job
+#: model:ir.model,name:queue_job.model_queue_job_lock
+msgid "Queue Job Lock"
+msgstr ""
+
#. module: queue_job
#. odoo-python
#: code:addons/queue_job/models/queue_job.py:0
@@ -941,6 +941,13 @@ msgstr "重新排队向导所选的作业"
msgid "Worker Pid"
msgstr "工作进程PID"
+#, python-format
+#~ msgid "If both parameters are 0, ALL jobs will be requeued!"
+#~ msgstr "如果两个参数都为0,所有任务都将被重新排队!"
+
+#~ msgid "Jobs Garbage Collector"
+#~ msgstr "作业垃圾收集器"
+
#~ msgid "SMS Delivery error"
#~ msgstr "短信传递错误"
diff --git a/queue_job/job.py b/queue_job/job.py
index 9843c01f05..e03dd2b517 100644
--- a/queue_job/job.py
+++ b/queue_job/job.py
@@ -238,6 +238,61 @@ def load_many(cls, env, job_uuids):
recordset = cls.db_records_from_uuids(env, job_uuids)
return {cls._load_from_db_record(record) for record in recordset}
+ def add_lock_record(self):
+ """
+ Create row in db to be locked while the job is being performed.
+ """
+ self.env.cr.execute(
+ """
+ INSERT INTO
+ queue_job_lock (id, queue_job_id)
+ SELECT
+ id, id
+ FROM
+ queue_job
+ WHERE
+ uuid = %s
+ ON CONFLICT(id)
+ DO NOTHING;
+ """,
+ [self.uuid],
+ )
+
+ def lock(self):
+ """
+ Lock row of job that is being performed
+
+ If a job cannot be locked,
+ it means that the job wasn't started,
+ a RetryableJobError is thrown.
+ """
+ self.env.cr.execute(
+ """
+ SELECT
+ *
+ FROM
+ queue_job_lock
+ WHERE
+ queue_job_id in (
+ SELECT
+ id
+ FROM
+ queue_job
+ WHERE
+ uuid = %s
+ AND state='started'
+ )
+ FOR UPDATE;
+ """,
+ [self.uuid],
+ )
+
+ # 1 job should be locked
+ if 1 != len(self.env.cr.fetchall()):
+ raise RetryableJobError(
+ f"Trying to lock job that wasn't started, uuid: {self.uuid}"
+ )
+
@classmethod
def _load_from_db_record(cls, job_db_record):
stored = job_db_record
@@ -819,6 +874,7 @@ def set_started(self):
self.state = STARTED
self.date_started = datetime.now()
self.worker_pid = os.getpid()
+ self.add_lock_record()
def set_done(self, result=None):
self.state = DONE
diff --git a/queue_job/jobrunner/runner.py b/queue_job/jobrunner/runner.py
index 47417caa4f..48bf51020e 100644
--- a/queue_job/jobrunner/runner.py
+++ b/queue_job/jobrunner/runner.py
@@ -114,22 +114,6 @@
* After creating a new database or installing queue_job on an
existing database, Odoo must be restarted for the runner to detect it.
-* When Odoo shuts down normally, it waits for running jobs to finish.
- However, when the Odoo server crashes or is otherwise force-stopped,
- running jobs are interrupted while the runner has no chance to know
- they have been aborted. In such situations, jobs may remain in
- ``started`` or ``enqueued`` state after the Odoo server is halted.
- Since the runner has no way to know if they are actually running or
- not, and does not know for sure if it is safe to restart the jobs,
- it does not attempt to restart them automatically. Such stale jobs
- therefore fill the running queue and prevent other jobs to start.
- You must therefore requeue them manually, either from the Jobs view,
- or by running the following SQL statement *before starting Odoo*:
-
-.. code-block:: sql
-
- update queue_job set state='pending' where state in ('started', 'enqueued')
-
.. rubric:: Footnotes
.. [1] From a security standpoint, it is safe to have an anonymous HTTP
@@ -155,16 +139,21 @@
from odoo.tools import config
from . import queue_job_config
-from .channels import ENQUEUED, NOT_DONE, PENDING, ChannelManager
+from .channels import ENQUEUED, NOT_DONE, ChannelManager
SELECT_TIMEOUT = 60
ERROR_RECOVERY_DELAY = 5
+PG_ADVISORY_LOCK_ID = 2293787760715711918
_logger = logging.getLogger(__name__)
select = selectors.DefaultSelector
+class MasterElectionLost(Exception):
+ pass
+
+
# Unfortunately, it is not possible to extend the Odoo
# server command line arguments, so we resort to environment variables
# to configure the runner (channels mostly).
@@ -207,28 +196,6 @@ def _connection_info_for(db_name):
def _async_http_get(scheme, host, port, user, password, db_name, job_uuid):
- # Method to set failed job (due to timeout, etc) as pending,
- # to avoid keeping it as enqueued.
- def set_job_pending():
- connection_info = _connection_info_for(db_name)
- conn = psycopg2.connect(**connection_info)
- conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
- with closing(conn.cursor()) as cr:
- cr.execute(
- "UPDATE queue_job SET state=%s, "
- "date_enqueued=NULL, date_started=NULL "
- "WHERE uuid=%s and state=%s "
- "RETURNING uuid",
- (PENDING, job_uuid, ENQUEUED),
- )
- if cr.fetchone():
- _logger.warning(
- "state of job %s was reset from %s to %s",
- job_uuid,
- ENQUEUED,
- PENDING,
- )
-
# TODO: better way to HTTP GET asynchronously (grequest, ...)?
# if this was python3 I would be doing this with
# asyncio, aiohttp and aiopg
@@ -236,6 +203,7 @@ def urlopen():
url = "{}://{}:{}/queue_job/runjob?db={}&job_uuid={}".format(
scheme, host, port, db_name, job_uuid
)
+ # pylint: disable=except-pass
try:
auth = None
if user:
@@ -249,10 +217,10 @@ def urlopen():
# for codes between 500 and 600
response.raise_for_status()
except requests.Timeout:
- set_job_pending()
+ # A timeout is a normal behaviour, it shouldn't be logged as an exception
+ pass
except Exception:
_logger.exception("exception in GET %s", url)
- set_job_pending()
thread = threading.Thread(target=urlopen)
thread.daemon = True
@@ -264,10 +232,15 @@ def __init__(self, db_name):
self.db_name = db_name
connection_info = _connection_info_for(db_name)
self.conn = psycopg2.connect(**connection_info)
- self.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
- self.has_queue_job = self._has_queue_job()
- if self.has_queue_job:
- self._initialize()
+ try:
+ self.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
+ self.has_queue_job = self._has_queue_job()
+ if self.has_queue_job:
+ self._acquire_master_lock()
+ self._initialize()
+ except BaseException:
+ self.close()
+ raise
def close(self):
# pylint: disable=except-pass
@@ -280,6 +253,14 @@ def close(self):
pass
self.conn = None
+ def _acquire_master_lock(self):
+ """Acquire the master runner lock or raise MasterElectionLost"""
+ with closing(self.conn.cursor()) as cr:
+ cr.execute("SELECT pg_try_advisory_lock(%s)", (PG_ADVISORY_LOCK_ID,))
+ if not cr.fetchone()[0]:
+ msg = f"could not acquire master runner lock on {self.db_name}"
+ raise MasterElectionLost(msg)
+
def _has_queue_job(self):
with closing(self.conn.cursor()) as cr:
cr.execute(
@@ -343,6 +324,93 @@ def set_job_enqueued(self, uuid):
(ENQUEUED, uuid),
)
+ def _query_requeue_dead_jobs(self):
+ return """
+ UPDATE
+ queue_job
+ SET
+ state=(
+ CASE
+ WHEN
+ max_retries IS NOT NULL AND
+ retry IS NOT NULL AND
+ retry>max_retries
+ THEN 'failed'
+ ELSE 'pending'
+ END),
+ retry=(
+ CASE
+ WHEN state='started'
+ THEN COALESCE(retry,0)+1 ELSE retry
+ END),
+ exc_name=(
+ CASE
+ WHEN
+ max_retries IS NOT NULL AND
+ retry IS NOT NULL AND
+ retry>max_retries
+ THEN 'JobFoundDead'
+ ELSE exc_name
+ END),
+ exc_info=(
+ CASE
+ WHEN
+ max_retries IS NOT NULL AND
+ retry IS NOT NULL AND
+ retry>max_retries
+ THEN 'Job found dead after too many retries'
+ ELSE exc_info
+ END)
+ WHERE
+ id in (
+ SELECT
+ queue_job_id
+ FROM
+ queue_job_lock
+ WHERE
+ queue_job_id in (
+ SELECT
+ id
+ FROM
+ queue_job
+ WHERE
+ state IN ('enqueued','started')
+ AND date_enqueued <
+ (now() AT TIME ZONE 'utc' - INTERVAL '10 sec')
+ )
+ FOR UPDATE SKIP LOCKED
+ )
+ RETURNING uuid
+ """
+
+ def requeue_dead_jobs(self):
+ """
+ Set started and enqueued jobs but not locked to pending
+
+ A job is locked when it's being executed
+ When a job is killed, it releases the lock
+
+ If the number of retries exceeds the number of max retries,
+ the job is set as 'failed' with the error 'JobFoundDead'.
+
+ Adding a buffer on 'date_enqueued' to check
+ that it has been enqueued for more than 10sec.
+ This prevents from requeuing jobs before they are actually started.
+
+ When Odoo shuts down normally, it waits for running jobs to finish.
+ However, when the Odoo server crashes or is otherwise force-stopped,
+ running jobs are interrupted while the runner has no chance to know
+ they have been aborted.
+ """
+
+ with closing(self.conn.cursor()) as cr:
+ query = self._query_requeue_dead_jobs()
+
+ cr.execute(query)
+
+ for (uuid,) in cr.fetchall():
+ _logger.warning("Re-queued dead job with uuid: %s", uuid)
+
class QueueJobRunner:
def __init__(
@@ -415,7 +483,8 @@ def close_databases(self, remove_jobs=True):
self.db_by_name = {}
def initialize_databases(self):
- for db_name in self.get_db_names():
+ for db_name in sorted(self.get_db_names()):
+ # sorting is important to avoid deadlocks in acquiring the master lock
db = Database(db_name)
if db.has_queue_job:
self.db_by_name[db_name] = db
@@ -423,6 +492,13 @@ def initialize_databases(self):
for job_data in cr:
self.channel_manager.notify(db_name, *job_data)
_logger.info("queue job runner ready for db %s", db_name)
+ else:
+ db.close()
+
+ def requeue_dead_jobs(self):
+ for db in self.db_by_name.values():
+ if db.has_queue_job:
+ db.requeue_dead_jobs()
def run_jobs(self):
now = _odoo_now()
@@ -509,13 +585,14 @@ def run(self):
while not self._stop:
# outer loop does exception recovery
try:
- _logger.info("initializing database connections")
+ _logger.debug("initializing database connections")
# TODO: how to detect new databases or databases
# on which queue_job is installed after server start?
self.initialize_databases()
_logger.info("database connections ready")
# inner loop does the normal processing
while not self._stop:
+ self.requeue_dead_jobs()
self.process_notifications()
self.run_jobs()
self.wait_notification()
@@ -524,6 +601,14 @@ def run(self):
except InterruptedError:
# Interrupted system call, i.e. KeyboardInterrupt during select
self.stop()
+ except MasterElectionLost as e:
+ _logger.debug(
+ "master election lost: %s, sleeping %ds and retrying",
+ e,
+ ERROR_RECOVERY_DELAY,
+ )
+ self.close_databases()
+ time.sleep(ERROR_RECOVERY_DELAY)
except Exception:
_logger.exception(
"exception: sleeping %ds and retrying", ERROR_RECOVERY_DELAY
diff --git a/queue_job/migrations/17.0.1.2.0/pre-migration.py b/queue_job/migrations/17.0.1.2.0/pre-migration.py
new file mode 100644
index 0000000000..8dbb6ff7f1
--- /dev/null
+++ b/queue_job/migrations/17.0.1.2.0/pre-migration.py
@@ -0,0 +1,22 @@
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
+
+
+def migrate(cr, version):
+ # Deactivate cron garbage collector
+ cr.execute(
+ """
+ UPDATE
+ ir_cron
+ SET
+ active=False
+ WHERE id IN (
+ SELECT res_id
+ FROM
+ ir_model_data
+ WHERE
+ module='queue_job'
+ AND model='ir.cron'
+ AND name='ir_cron_queue_job_garbage_collector'
+ );
+ """
+ )
diff --git a/queue_job/models/__init__.py b/queue_job/models/__init__.py
index 4744e7ab46..6265dfe9cb 100644
--- a/queue_job/models/__init__.py
+++ b/queue_job/models/__init__.py
@@ -3,3 +3,4 @@
from . import queue_job
from . import queue_job_channel
from . import queue_job_function
+from . import queue_job_lock
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index 7607a2701f..33dbf2346d 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -6,7 +6,6 @@
from datetime import datetime, timedelta
from odoo import _, api, exceptions, fields, models
-from odoo.osv import expression
from odoo.tools import config, html_escape
from odoo.addons.base_sparse_field.models.fields import Serialized
@@ -414,55 +413,6 @@ def autovacuum(self):
break
return True
- def requeue_stuck_jobs(self, enqueued_delta=5, started_delta=0):
- """Fix jobs that are in a bad states
-
- :param in_queue_delta: lookup time in minutes for jobs
- that are in enqueued state
-
- :param started_delta: lookup time in minutes for jobs
- that are in enqueued state,
- 0 means that it is not checked
- """
- self._get_stuck_jobs_to_requeue(
- enqueued_delta=enqueued_delta, started_delta=started_delta
- ).requeue()
- return True
-
- def _get_stuck_jobs_domain(self, queue_dl, started_dl):
- domain = []
- now = fields.datetime.now()
- if queue_dl:
- queue_dl = now - timedelta(minutes=queue_dl)
- domain.append(
- [
- "&",
- ("date_enqueued", "<=", fields.Datetime.to_string(queue_dl)),
- ("state", "=", "enqueued"),
- ]
- )
- if started_dl:
- started_dl = now - timedelta(minutes=started_dl)
- domain.append(
- [
- "&",
- ("date_started", "<=", fields.Datetime.to_string(started_dl)),
- ("state", "=", "started"),
- ]
- )
- if not domain:
- raise exceptions.ValidationError(
- _("If both parameters are 0, ALL jobs will be requeued!")
- )
- return expression.OR(domain)
-
- def _get_stuck_jobs_to_requeue(self, enqueued_delta, started_delta):
- job_model = self.env["queue.job"]
- stuck_jobs = job_model.search(
- self._get_stuck_jobs_domain(enqueued_delta, started_delta)
- )
- return stuck_jobs
-
def related_action_open_record(self):
"""Open a form view with the record(s) of the job.
diff --git a/queue_job/models/queue_job_lock.py b/queue_job/models/queue_job_lock.py
new file mode 100644
index 0000000000..b01c7f3a91
--- /dev/null
+++ b/queue_job/models/queue_job_lock.py
@@ -0,0 +1,16 @@
+# Copyright 2025 ACSONE SA/NV
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class QueueJobLock(models.Model):
+ _name = "queue.job.lock"
+ _description = "Queue Job Lock"
+
+ queue_job_id = fields.Many2one(
+ comodel_name="queue.job",
+ required=True,
+ ondelete="cascade",
+ index=True,
+ )
diff --git a/queue_job/readme/CONFIGURE.md b/queue_job/readme/CONFIGURE.md
index 07b7b84126..216b5358af 100644
--- a/queue_job/readme/CONFIGURE.md
+++ b/queue_job/readme/CONFIGURE.md
@@ -35,3 +35,6 @@ channels = root:2
[^1]: It works with the threaded Odoo server too, although this way of
running Odoo is obviously not for production purposes.
+
+* Jobs that remain in `enqueued` or `started` state (because, for instance,
+ their worker has been killed) will be automatically re-queued.
diff --git a/queue_job/security/ir.model.access.csv b/queue_job/security/ir.model.access.csv
index 634daf8ede..4def7dc38a 100644
--- a/queue_job/security/ir.model.access.csv
+++ b/queue_job/security/ir.model.access.csv
@@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_queue_job_manager,queue job manager,queue_job.model_queue_job,queue_job.group_queue_job_manager,1,1,1,1
+access_queue_job_lock_manager,queue job lock manager,queue_job.model_queue_job_lock,queue_job.group_queue_job_manager,1,0,0,0
access_queue_job_function_manager,queue job functions manager,queue_job.model_queue_job_function,queue_job.group_queue_job_manager,1,1,1,1
access_queue_job_channel_manager,queue job channel manager,queue_job.model_queue_job_channel,queue_job.group_queue_job_manager,1,1,1,1
access_queue_requeue_job,queue requeue job manager,queue_job.model_queue_requeue_job,queue_job.group_queue_job_manager,1,1,1,1
diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html
index 8fb0f6b0e1..d4acc2b9ca 100644
--- a/queue_job/static/description/index.html
+++ b/queue_job/static/description/index.html
@@ -367,7 +367,7 @@ Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:985afa6fddcad78278ffe6f760e0483e547b2dc57dad1d829187d485ac1a22cb
+!! source digest: sha256:56ab2288bc8332ef24cb27d98f8851066a87bd7a84c314094a99edef659d608a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

This addon adds an integrated Job Queue to Odoo.
@@ -376,19 +376,19 @@ Job Queue
transaction.
Example:
-from odoo import models, fields, api
+from odoo import models, fields, api
-class MyModel(models.Model):
+class MyModel(models.Model):
_name = 'my.model'
- def my_method(self, a, k=None):
+ def my_method(self, a, k=None):
_logger.info('executed with a: %s and k: %s', a, k)
-class MyOtherModel(models.Model):
+class MyOtherModel(models.Model):
_name = 'my.other.model'
- def button_do_stuff(self):
+ def button_do_stuff(self):
self.env['my.model'].with_delay().my_method('a', k=2)
In the snippet of code above, when we call button_do_stuff, a job
@@ -517,6 +517,9 @@
immediately and in parallel.
Tip: to enable debug logging for the queue job, use
--log-handler=odoo.addons.queue_job:DEBUG
+Jobs that remain in enqueued or started state (because, for
+instance, their worker has been killed) will be automatically
+re-queued.