TIP: You can type at any time to perform a new search.
Job Queue
queue_job · OCA/queue
🛠 Migration considerations
- Raw `cr.execute()` INSERT/UPDATE/DELETE bypasses the ORM (no compute/constrains/tracking/mail): `DROP TRIGGER IF EXISTS queue_job_notify ON queue_job; CREATE OR REPLACE FUNCTION queue_job_notify() RETURNS trigger AS $$ BEGIN IF TG_OP = 'DELETE' THEN IF OLD.state != 'done' THEN PERFORM pg_notify('queue_job', OLD.uuid); END IF; ELSE PERFORM pg_notify('queue_job', NEW.uuid); END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER queue_job_notify AFTER INSERT OR UPDATE OR DELETE ON queue_job FOR EACH ROW EXECUTE PROCEDURE queue_job_notify();` - re-check the table/column names still match after upgrading. migration-raw-sql-write
- Raw `cr.execute()` INSERT/UPDATE/DELETE bypasses the ORM (no compute/constrains/tracking/mail): `INSERT INTO queue_job_lock (id, queue_job_id) SELECT id, id FROM queue_job WHERE uuid = %s ON CONFLICT(id) DO NOTHING;` - re-check the table/column names still match after upgrading. migration-raw-sql-write
- Raw `cr.execute()` INSERT/UPDATE/DELETE bypasses the ORM (no compute/constrains/tracking/mail): `SELECT * FROM queue_job_lock WHERE queue_job_id in ( SELECT id FROM queue_job WHERE uuid = %s AND state = %s ) FOR NO KEY UPDATE SKIP LOCKED;` - re-check the table/column names still match after upgrading. migration-raw-sql-write
- Raw `cr.execute()` INSERT/UPDATE/DELETE bypasses the ORM (no compute/constrains/tracking/mail): `UPDATE queue_job SET state=%s, date_enqueued=date_trunc('seconds', now() at time zone 'utc') WHERE uuid=%s` - re-check the table/column names still match after upgrading. migration-raw-sql-write
- Raw `cr.execute()` INSERT/UPDATE/DELETE bypasses the ORM (no compute/constrains/tracking/mail): `SELECT uuid FROM queue_job WHERE uuid=%s AND state=%s FOR NO KEY UPDATE SKIP LOCKED` - re-check the table/column names still match after upgrading. migration-raw-sql-write
Found by automated static analysis: patterns worth a look before/after upgrading this module to a newer Odoo version.
- Repository
- OCA/queue · module folder · Try on Runboat
- Module version
- 2.0.3
- Category
- Generic Modules
- Folder size
- 1.33 MB
- License
- LGPL-3
- Application
- No
- Auto-installable
- No
- Website
- https://github.com/OCA/queue
- Last tracking update
- 2026-08-12 05:23:56
- Authors
- Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)
- Maintainers
- Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)
- Committers
- Stéphane Bidoul, Guewen Baconnier, Alexandre Fayolle, GitHub, Weblate, OCA-git-bot, oca-ci, Fernando, Danny W. Adair, Milan Topuzov
- Odoo dependencies
- Python dependencies
- openupgradelib, requests
- System dependencies
- None
- Required by
- base_import_async, connector, connector_importer, edi_queue_oca, partner_invoicing_mode, partner_invoicing_mode_at_shipping, queue_job_batch, queue_job_cron, queue_job_cron_jobrunner, queue_job_subscribe, report_async, sale_automatic_workflow_job, shopify_connector, test_queue_job
- Description
This addon adds an integrated Job Queue to Odoo. It allows to postpone method calls executed asynchronously. Jobs are executed in the background by a `Jobrunner`, in their own transaction. Example: ``` python from odoo import models, fields, api class MyModel(models.Model): _name = 'my.model' def my_method(self, a, k=None): _logger.info('executed with a: %s and k: %s', a, k) class MyOtherModel(models.Model): _name = 'my.other.model' 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 **capturing the method and arguments** will be postponed. It will be executed as soon as the Jobrunner has a free bucket, which can be instantaneous if no other job is running. Features: - Views for jobs, jobs are stored in PostgreSQL - Jobrunner: execute the jobs, highly efficient thanks to PostgreSQL's NOTIFY - Channels: give a capacity for the root channel and its sub-channels and segregate jobs in them. Allow for instance to restrict heavy jobs to be executed one at a time while little ones are executed 4 at a times. - Retries: Ability to retry jobs by raising a type of exception - Retry Pattern: the 3 first tries, retry after 10 seconds, the 5 next tries, retry after 1 minutes, ... - Job properties: priorities, estimated time of arrival (ETA), custom description, number of retries - Related Actions: link an action on the job view, such as open the record concerned by the job
Code Analysis ⓘ
Views touched (14)
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
view_queue_job_channel_form |
queue.job.channel.form | queue.job.channel | form | New |
view_queue_job_channel_search |
queue.job.channel.search | queue.job.channel | search | New |
view_queue_job_channel_tree |
queue.job.channel.tree | queue.job.channel | list | New |
view_queue_job_form |
queue.job.form | queue.job | form | New |
view_queue_job_function_form |
queue.job.function.form | queue.job.function | form | New |
view_queue_job_function_search |
queue.job.function.search | queue.job.function | search | New |
view_queue_job_function_tree |
queue.job.function.tree | queue.job.function | list | New |
view_queue_job_graph |
queue.job.graph | queue.job | graph | New |
view_queue_job_pivot |
queue.job.pivot | queue.job | pivot | New |
view_queue_job_search |
queue.job.search | queue.job | search | New |
view_queue_job_tree |
queue.job.tree | queue.job | list | New |
view_requeue_job |
Requeue Jobs | queue.requeue.job | form | New |
view_set_jobs_cancelled |
Cancel Jobs | queue.jobs.to.cancelled | form | New |
view_set_jobs_done |
Set Jobs to Done | queue.jobs.to.done | form | New |
HTTP endpoints (2)
| Route(s) | Handler | Auth | Type | Methods | Flags |
|---|---|---|---|---|---|
/queue_job/create_test_job |
RunJobController.create_test_job |
user | http | ALL | |
/queue_job/runjob |
RunJobController.runjob |
none | http | ALL |
Models touched (9)
New fields (0)
No new fields.
Public methods (2)-
delayable(self, priority=None, eta=None, max_retries=None, description=None, channel=None, identity_key=None)Return a ``Delayable`` The returned instance allows to enqueue any method of the recordset's Model. Usage:: delayable = self.env["res.users"].browse(10).delayable(priority=20) delayable.do_work(name="test"}).delay() In this example, the ``do_work`` method will not be executed directly. It will be executed in an asynchronous job. Method calls on a Delayable generally return themselves, so calls can be chained together:: delayable.set(priority=15).do_work(name="test"}).delay() The order of the calls that build the job is not relevant, beside the call to ``delay()`` that must happen at the very end. This is equivalent to the example above:: delayable.do_work(name="test"}).set(priority=15).delay() Very importantly, ``delay()`` must be called on the top-most parent of a chain of jobs, so if you have this:: job1 = record1.delayable().do_work() job2 = record2.delayable().do_work() job1.on_done(job2) The ``delay()`` call must be made on ``job1``, otherwise ``job2`` will be delayed, but ``job1`` will never be. When done on ``job1``, the ``delay()`` call will traverse the graph of jobs and delay all of them:: job1.delay() For more details on the graph dependencies, read the documentation of :module:`~odoo.addons.queue_job.delay`. :param priority: Priority of the job, 0 being the higher priority. Default is 10. :param eta: Estimated Time of Arrival of the job. It will not be executed before this date/time. :param max_retries: maximum number of retries before giving up and set the job state to 'failed'. A value of 0 means infinite retries. Default is 5. :param description: human description of the job. If None, description is computed from the function doc or name :param channel: the complete name of the channel to use to process the function. If specified it overrides the one defined on the function :param identity_key: key uniquely identifying the job, if specified and a job with the same key has not yet been run, the new job will not be added. It is either a string, either a function that takes the job as argument (see :py:func:`..job.identity_exact`). the new job will not be added. :return: instance of a Delayable :rtype: :class:`odoo.addons.queue_job.job.Delayable` -
with_delay(self, priority=None, eta=None, max_retries=None, description=None, channel=None, identity_key=None)Return a ``DelayableRecordset`` It is a shortcut for the longer form as shown below:: self.with_delay(priority=20).action_done() # is equivalent to: self.delayable().set(priority=20).action_done().delay() ``with_delay()`` accepts job properties which specify how the job will be executed. Usage with job properties:: env['a.model'].with_delay(priority=30, eta=60*60*5).action_done() delayable.export_one_thing(the_thing_to_export) # => the job will be executed with a low priority and not before a # delay of 5 hours from now When using :meth:``with_delay``, the final ``delay()`` is implicit. See the documentation of :meth:``delayable`` for more details. :return: instance of a DelayableRecordset :rtype: :class:`odoo.addons.queue_job.job.DelayableRecordset`
New fields (1)
-
ttypeSelectionondelete={'job_serialized': 'cascade'}selection_add=[('job_serialized', 'Job Serialized')]
No public methods.
New fields (29)
-
channelCharindex=True -
channel_method_nameCharreadonly=Truestring='Complete Method Name' -
company_idMany2one → res.companycomodel_name='res.company'index=Truestring='Company' -
date_cancelledDatetimereadonly=True -
date_createdDatetimereadonly=Truestring='Created Date' -
date_doneDatetimereadonly=True -
date_enqueuedDatetimereadonly=Truestring='Enqueue Time' -
date_startedDatetimereadonly=Truestring='Start Date' -
etaDatetimestring='Execute only after' -
exc_infoTextreadonly=Truestring='Exception Info' -
exc_messageCharreadonly=Truestring='Exception Message'tracking=True -
exc_nameCharreadonly=Truestring='Exception' -
exec_timeFloataggregator='avg'help='Time required to execute this job in seconds. Average when grouped.'readonly=Truestring='Execution Time (avg)' -
func_stringCharreadonly=Truestring='Task' -
graph_jobs_countIntegercompute='_compute_graph_jobs_count' -
graph_uuidCharhelp='Single shared identifier of a Graph. Empty for a single job.'index=Truereadonly=Truestring='Graph UUID' -
identity_keyCharreadonly=True -
job_function_idMany2one → queue.job.functioncomodel_name='queue.job.function'readonly=Truestring='Job Function' -
max_retriesIntegerhelp='The job will fail if the number of tries reach the max. retries.\nRetries are infinite when empty.'string='Max. retries' -
method_nameCharreadonly=True -
model_nameCharreadonly=Truestring='Model' -
nameCharreadonly=Truestring='Description' -
priorityIntegeraggregator=False -
resultTextreadonly=True -
retryIntegerstring='Current try' -
stateSelectionindex=Truereadonly=Truerequired=True args: STATES -
user_idMany2one → res.userscomodel_name='res.users'string='User ID' -
uuidCharindex=Truereadonly=Truerequired=Truestring='UUID' -
worker_pidIntegerreadonly=True
-
autovacuum(self)Delete all jobs done based on the removal interval defined on the channel Called from a cron. -
button_cancelled(self) -
button_done(self) -
create(self, vals_list)@api.model_create_multi@api.private -
init(self) -
open_graph_jobs(self)Return action that opens all jobs of the same graph -
open_related_action(self)Open the related action associated to the job -
related_action_open_record(self)Open a form view with the record(s) of the job. For instance, for a job on a ``product.product``, it will open a ``product.product`` form view with the product record(s) concerned by the job. If the job concerns more than one record, it opens them in a list. This is the default related action. -
requeue(self) -
write(self, vals)
New fields (5)
-
complete_nameCharcompute='_compute_complete_name'readonly=Truerecursive=Truestore=True -
job_function_idsOne2many → queue.job.functioncomodel_name='queue.job.function'inverse_name='channel_id'string='Job Functions' -
nameChar -
parent_idMany2one → queue.job.channelcomodel_name='queue.job.channel'ondelete='restrict'string='Parent Channel' -
removal_intervalIntegerdefault=<expr>required=True
-
create(self, vals_list)@api.model_create_multi -
parent_required(self)@api.constrains('parent_id', 'name') -
write(self, values)
New fields (8)
-
allow_commitBooleanhelp='Allows the job to commit transactions during execution. Under the hood, this executes the job in a new database cursor, which incurs an overhead as it requires an extra connection to the database. ' -
channelCharreadonly=Truerelated='channel_id.complete_name'store=True -
channel_idMany2one → queue.job.channelcomodel_name='queue.job.channel'default=<expr>required=Truestring='Channel' -
edit_related_actionTextcompute='_compute_edit_related_action'help='The action when the button *Related Action* is used on a job. The default action is to open the view of the record related to the job. Configured as a dictionary with optional keys: enable, func_name, kwargs.\nSee the module description for details.'inverse='_inverse_edit_related_action'string='Related Action' -
edit_retry_patternTextcompute='_compute_edit_retry_pattern'help='Pattern expressing from the count of retries on retryable errors, the number of of seconds to postpone the next execution. Setting the number of seconds to a 2-element tuple or list will randomize the retry interval between the 2 values.\nExample: {1: 10, 5: 20, 10: 30, 15: 300}.\nExample: {1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}.\nSee the module description for details.'inverse='_inverse_edit_retry_pattern'string='Retry Pattern' -
methodChar -
model_idMany2one → ir.modelcomodel_name='ir.model'ondelete='cascade'string='Model' -
nameCharcompute='_compute_name'index=Trueinverse='_inverse_name'store=True
-
create(self, vals_list)@api.model_create_multi -
job_config(self, name)@tools.ormcache('name') -
job_default_config(self) -
job_function_name(model_name, method_name)@staticmethod -
unlink(self) -
write(self, values)
New fields (1)
-
queue_job_idMany2one → queue.jobcomodel_name='queue.job'index=Trueondelete='cascade'required=True
No public methods.
New fields (0)
No new fields.
Public methods (1)-
set_cancelled(self)
New fields (0)
No new fields.
Public methods (1)-
set_done(self)
New fields (1)
-
job_idsMany2many → queue.jobcomodel_name='queue.job'default=<expr>string='Jobs'
-
requeue(self)
Loading…
Loading…
Loading…
Loading…
Loading…
Loading…
Loading…
Loading…
Loading…