Repository navigation
[19.0] [FIX] queue_job: set date_cancelled on cancelled dependent jobs
#1015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 19.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -546,48 +546,45 @@ def in_temporary_env(self): | |||
| finally: | ||||
| self._env = env | ||||
|
|
||||
| def _get_common_dependent_jobs_query(self): | ||||
| return """ | ||||
| UPDATE queue_job | ||||
| SET state = %s | ||||
| FROM ( | ||||
| SELECT child.id, array_agg(parent.state) as parent_states | ||||
| FROM queue_job job | ||||
| JOIN LATERAL | ||||
| json_array_elements_text( | ||||
| job.dependencies::json->'reverse_depends_on' | ||||
| ) child_deps ON true | ||||
| JOIN queue_job child | ||||
| ON child.graph_uuid = job.graph_uuid | ||||
| AND child.uuid = child_deps | ||||
| JOIN LATERAL | ||||
| json_array_elements_text( | ||||
| child.dependencies::json->'depends_on' | ||||
| ) parent_deps ON true | ||||
| JOIN queue_job parent | ||||
| ON parent.graph_uuid = job.graph_uuid | ||||
| AND parent.uuid = parent_deps | ||||
| WHERE job.uuid = %s | ||||
| GROUP BY child.id | ||||
| ) jobs | ||||
| WHERE | ||||
| queue_job.id = jobs.id | ||||
| AND %s = ALL(jobs.parent_states) | ||||
| AND state = %s; | ||||
| """ | ||||
|
|
||||
| def should_check_dependents(self): | ||||
| return any(self.__reverse_depends_on_uuids) | ||||
|
|
||||
| def enqueue_waiting(self): | ||||
| sql = self._get_common_dependent_jobs_query() | ||||
| self.env.cr.execute(sql, (PENDING, self.uuid, DONE, WAIT_DEPENDENCIES)) | ||||
| self.env["queue.job"].invalidate_model(["state"]) | ||||
| dependents = ( | ||||
| self.env["queue.job"] | ||||
| .sudo() | ||||
| .search( | ||||
| [ | ||||
| ("graph_uuid", "=", self.graph_uuid), | ||||
| ("state", "=", WAIT_DEPENDENCIES), | ||||
| ("dependency_ids", "any", [("uuid", "=", self.uuid)]), | ||||
| ("dependency_ids", "not any", [("state", "!=", DONE)]), | ||||
| ] | ||||
| ) | ||||
| ) | ||||
| dependents.state = PENDING | ||||
|
|
||||
| def cancel_dependent_jobs(self): | ||||
| sql = self._get_common_dependent_jobs_query() | ||||
| self.env.cr.execute(sql, (CANCELLED, self.uuid, CANCELLED, WAIT_DEPENDENCIES)) | ||||
| self.env["queue.job"].invalidate_model(["state"]) | ||||
| """Cancel the jobs waiting on this one, and their own dependents | ||||
|
|
||||
| A dependent job can no longer run once one of its dependencies is | ||||
| cancelled, whatever the state of the others. | ||||
| """ | ||||
| dependents = self.db_record().dependent_ids | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method run in the env of the job's user, which may not have access to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought that So all good |
||||
| while dependents: | ||||
| dependents = dependents.filtered( | ||||
| lambda dependent: dependent.state == WAIT_DEPENDENCIES | ||||
| ) | ||||
| dependents.write( | ||||
| { | ||||
| "state": CANCELLED, | ||||
| "date_cancelled": self.date_cancelled, | ||||
| "result": self.env._( | ||||
| "Cancelled because a job it depends on was cancelled" | ||||
| ), | ||||
| } | ||||
| ) | ||||
| dependents = dependents.dependent_ids | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good to add the recursion. NOTE: the The Power of 10 would suggest to define a max number of iteration, in particular when we don't ensure no cycle ever happen. How deep do we expect to go? If considerably deep: SQL recursioin would be better.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It always ends. Each pass cancels every job it keeps, and only waiting jobs are kept, so each job is handled at most once. That holds even with a cycle, e.g: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory yes, for _ in range(MAX_DEPTH):
if not dependents:
breakI won't push on this if you want to keep it. |
||||
|
|
||||
| def store(self): | ||||
| """Store the Job""" | ||||
|
|
||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is changing:
Before: We cancel a job only if all its parents were cancelled
After: We cancel all the dependents immediately
This has an impact when a jobs X depends on 2 other jobs Y and Z.
Lost benefit
Before: if we cancel Y and requeue it before cancelling Z, then X was not cancelled
After: X is cancelled when Y is cancelled.
This can be useful when things are stuck IMO
Fixed bug
If Y was cancelled but Z ran, then X would never be cancelled and never run -> stuck
2 options
Accept the loss
If we are fine losing the benefit, we can go this way
Keep both benefits
We should cancel X once:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixing the bug is precisely my intention here.
Why would you cancel before requeuing? I don't think we loose anything here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the potential reason:
IMO, it's not a big loss, we can go with that,
but it's worth mentioning that the behavior is changing if someone is wondering.