[WIP] FEAT! Make queue_job dependencies optional#2105
Conversation
ecino
commented
Jun 25, 2026
- NEW module queue_job_sh_safe that will safely use queue_jobs when available and fallback to an alternate mechanism with ir_cron in case the module is not installed.
- This is useful for Nordic using odoo.sh on which queue jobs are prohibited and not optimal for the infrastructure
There was a problem hiding this comment.
Code Review
This pull request introduces the queue_job_sh_safe module, which abstracts the OCA queue_job module to provide a fallback database-backed queue processed via a cron job when queue_job is not installed. This allows compatibility between dedicated environments and odoo.sh. Other modules are updated to depend on this new module and use the with_delay_sh method. Feedback on the changes highlights a critical transaction handling issue where exceptions caught inside the context manager could lead to partial commits, a performance bottleneck from instantiating a new registry with Registry.new(), and another bottleneck from querying ir.module.module on every delay call. Additionally, suggestions were made to use sudo() to prevent potential access errors for regular users and to dynamically filter unsupported fields during record creation.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Confidence Score: 1/5The queue fallback has multiple runtime paths that can fail or run work with the wrong execution context.
queue_job_sh_safe/models/base.py, queue_job_sh_safe/models/queue_job_replacement.py, and partner_communication/manifest.py
What T-Rex did
Reviews (3): Last reviewed commit: "[WIP] FEAT! Make queue_job dependencies ..." | Re-trigger Greptile |
07f6d5f to
a0cad46
Compare
| @@ -0,0 +1,3 @@ | |||
| id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | |||
| access_queue_job_replacement_group,queue.job.replacement.all,model_queue_job_replacement,queue_job_sh_safe.group_queue_job_replacement_user,1,1,1,1 | |||
There was a problem hiding this comment.
Executable Jobs Remain Editable
The new access group still has full create and write access to rows that cron_run_jobs() executes from stored res_model, res_ids, job_function, and job_args. When an operator is assigned this UI group, they can create or edit a replacement job that the cron later runs as stored executable work, so the dispatch path still needs a read-only/operator split or an allowlist tied to jobs created by with_delay_sh.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| "base_report_to_printer", # OCA/report-print-send | ||
| "contacts", | ||
| "queue_job", # OCA/queue | ||
| "queue_job_sh_safe", # OCA/queue |
There was a problem hiding this comment.
This manifest no longer installs queue_job, but the automated-action code in partner_communication/models/ir_actions.py still searches self.env["queue.job"] and calls self.with_delay(...). In the intended no-queue_job deployment, triggering that action raises at runtime instead of using the replacement queue.
- NEW module queue_job_sh_safe that will safely use queue_jobs when available and fallback to an alternate mechanism with ir_cron in case the module is not installed. - This is useful for Nordic using odoo.sh on which queue jobs are prohibited and not optimal for the infrastructure
a0cad46 to
e2d8348
Compare
| "user_id": self.env.user.id, | ||
| **delay_args, | ||
| } | ||
| for i in range(0, len(self), min(split, 1)) |
There was a problem hiding this comment.
Unsplit Jobs Crash Immediately
When a caller omits split, split defaults to 0, so this fallback builds range(0, len(self), 0) and raises ValueError before creating any replacement job. The migrated S2B button calls with_delay_sh("generate_letters_job", identity_key=...) without split, so no-queue_job deployments show a traceback and never queue the letter generation.
| while job_new_env and not job_new_env.is_predecessor_complete: | ||
| job_new_env = job_new_env.parent_job_id | ||
| if not job_new_env: | ||
| continue | ||
| job_new_env.state = "processing" | ||
| records = new_env[job.res_model].browse(literal_eval(job.res_ids)) | ||
| job_function = getattr(records, job.job_function) | ||
| job_result = job_function(*literal_eval(job.job_args)) | ||
| job_new_env.write({"state": "done", "job_result": str(job_result)}) |
There was a problem hiding this comment.
When a chained child is pending and its parent is failed or still not done, this loop walks job_new_env to the parent but still executes the original child's stored method. The cron then writes processing and done onto the parent with the child's result while leaving the child pending, so the GMC chained batches can revive a failed predecessor and rerun the child on the next cron cycle.
| records = new_env[job.res_model].browse(literal_eval(job.res_ids)) | ||
| job_function = getattr(records, job.job_function) | ||
| job_result = job_function(*literal_eval(job.job_args)) |
There was a problem hiding this comment.
with_delay_sh stores the enqueuing user_id, but cron dispatch browses records and calls the job method in the cron environment instead of switching to that user. In no-queue_job deployments, queued methods that normally depend on record rules or env.user now run as the cron/admin user and can perform work outside the original caller's permissions.
| "contacts", | ||
| "queue_job", # OCA/queue | ||
| "queue_job_sh_safe", # OCA/queue | ||
| "mass_mailing_sms", |
There was a problem hiding this comment.