[FIX] base_bg: do not let one job break the whole reaper - #437
Closed
nicomacr wants to merge 1 commit into
Closed
Conversation
Contributor
nicomacr
force-pushed
the
19.0-t-72579-nmr
branch
from
August 19, 2026 14:23
12401f0 to
1c6caf7
Compare
nicomacr
force-pushed
the
19.0-t-72579-nmr
branch
from
August 19, 2026 20:03
1c6caf7 to
bea3a6e
Compare
Failing a job whose target records were deleted raised MissingError: fail() browsed those ids to link them in the notification, and browsing a dropped id is truthy. That aborted the whole _cron_check_running_jobs loop, so the poisoned job stayed running forever and none of the other stuck jobs were ever handled. Check real existence before linking the records, and handle each job -- and the recovery path itself -- in its own savepoint, so an override that raises no longer takes the reaper down with it.
nicomacr
force-pushed
the
19.0-t-72579-nmr
branch
from
August 20, 2026 12:16
bea3a6e to
43031c8
Compare
JoelZilli
reviewed
Aug 21, 2026
JoelZilli
left a comment
Contributor
There was a problem hiding this comment.
Re-revisado después del amend. LGTM, sin bloqueos — dejo esto como comentario y no como Approve formal por ahora, pero de contenido está aprobado de mi lado.
Verificado
fail()con_get_records().exists(): la raíz queda cerrada y el camino que me preocupaba converge —run()→except→_handle_job_error→_give_up→failya no levanta, así que_cron_run_enqueued_jobsno aborta más. Aplica a jobs de cualquier modelo, como dice el body.- Fallback en su propio savepoint +
try/except: tenés razón en la corrección, el savepoint solo no alcanzaba porque la excepción se propagaba igual y mataba el loop. - Afuera el
invalidate_all(flush=False), con el comentario que explica por qué no hace falta. - Mi observación sobre
mapped()estaba mal y tu corrección es correcta: en Odoo 19,mapped(func)sobre un recordset vacío llamafunc(self)una vez con el recordset entero (orm/models.py:6174-6183), así que elr andsí cubría el caso vacío — lo que no cubría era el id borrado, que es el bug. La conclusión no cambia yif records:+ comprehension explícita es mejor que sacar el lambda a secas. - El cambio del test envenenado es acertado: con el fix de
fail(), borrar el partner ya no envenena nada, así que el test viejo hubiera pasado incluso sin el savepoint. Chequeé quepatch.object(type(self.BgJob), "fail", ...)no toca elBgJob.faildel fallback (clase de módulo, distinta de la del registry) y quebase_failse captura antes del patch: el test ejercita de verdad el camino de recuperación.
Nits
Tres comentarios inline, ninguno bloquea. Nada más de mi lado.
No corrí los tests localmente (habría que cambiar de rama en mi working tree); me apoyo en el CI. Verifiqué mapped() y la semántica de _FlushingSavepoint leyendo el core 19.
Contributor
Author
|
@roboadhoc r+ nobump |
roboadhoc
pushed a commit
that referenced
this pull request
Aug 21, 2026
Failing a job whose target records were deleted raised MissingError: fail() browsed those ids to link them in the notification, and browsing a dropped id is truthy. That aborted the whole _cron_check_running_jobs loop, so the poisoned job stayed running forever and none of the other stuck jobs were ever handled. Check real existence before linking the records, and handle each job -- and the recovery path itself -- in its own savepoint, so an override that raises no longer takes the reaper down with it. closes #437 Signed-off-by: Nicolas Mac Rouillon (ADV) <nmr@adhoc.inc>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problema
Un job cuyos registros objetivo ya no existen revienta al ser dado por vencido:
fail()browsea esos ids para linkearlos en la notificación y, como browsear un id borrado devuelve un recordset truthy, leer sudisplay_namelevantaMissingError. La excepción aborta el loop entero de_cron_check_running_jobs: la transacción se rollbackea, el job envenenado queda enrunningy ningún otro job colgado se procesa, en esa corrida ni en las siguientes.El mismo
fail()corre desderun()→_handle_job_error→_give_up, así que un job con su target borrado también aborta el cron del runner.Cambio
fail()chequea existencia real (_get_records().exists()) antes de linkear los registros en la notificación. Aplica a jobs de cualquier modelo._is_transient_error) → warning y se deja para la próxima corrida; cualquier otra cosa (típicamente un override del modelo que revienta) → el job se marcafailedcon las implementaciones base, sin notificar, y se cancela el resto del batch. Ese camino de recuperación corre en su propio savepoint, así que si también revienta el reaper sigue con los demás jobs vencidos.Tests
base_bg/tests/test_bg_job.py:test_fail_notifies_when_records_were_deleted:fail()sobre un job cuyos registros se borraron → quedafailedsin levantarMissingError.test_cron_check_running_jobs_skips_poisoned_job: un override defail()que revienta + un job sano → el envenenado quedafailedcon su batch cancelado y el sano se vence igual.test_cron_check_running_jobs_defers_transient_error:SerializationFailure→ el job quedarunningpara la próxima corrida.Tarea: https://www.adhoc.inc/odoo/project.task/72579