-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Simple task to finish inactive builds #3312
Conversation
Ah yes, this could be updated in our docs probably. We run celery beat just as we run celery:
So this would be another command to run for local development. You want to run on a separate worker, as it should only have 1 concurrent process. That is, this will result in multiple scheduled tasks, as there is 4 concurrency:
I'm fine not supporting this when running celery as Custom time limit should be 150-200% of the project build time. The project build time is the Also, thinking more on this, it might be helpful to include more information in the error message. Adding something similar to our general error reporting would help -- "If you continue to encounter this error, file a support request with and reference this build id (#)" |
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.
I think this is in the right direction. I added some feedback on the questions you've had so far, so we'll probably need another review when those changes are added.
readthedocs/projects/tasks.py
Outdated
for build in Build.objects.filter(query): | ||
build.success = False | ||
build.state = BUILD_STATE_FINISHED | ||
build.error = 'This build was terminated due to inactivity.' |
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.
On my PR, I'm accumulating these error messages in one spot, which might make sense. I'm storing these error messages all in a central projects.exceptions
file. This could be implemented as an exception, but I'm not sure if the exception will be raised otherwise -- we could combine the message for this and the timeout kill though.
readthedocs/projects/tasks.py
Outdated
query = (~Q(state=BUILD_STATE_FINISHED) & | ||
Q(date__lte=datetime.datetime.now() - datetime.timedelta(minutes=45))) | ||
# TODO: consider ``poject.container_time_limit`` since it could be bigger than 45 minutes | ||
for build in Build.objects.filter(query): |
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 could be build.update()
instead.
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.
If we are going to use update
we can't put the build id #
in the message.
So, for now, I'm adding this message:
error=('This build was terminated due to inactivity. If you continue '
'to encounter this error, file a support request.')
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.
Also, I think I will limit the query to the first 25 or 50 elements because otherwise the first time this task will be ran it will take tons of existing builds from all the times...
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.
Besides, this is not valid...
AssertionError: Cannot update a query once a slice has been taken.
So, maybe go back to my approach is better.
I think we should use celery beat for this. otherwise this task does not have any benifit |
Celery beat is going to be used. The task above is will be a scheduled task |
I just update this PR with:
Let me know if this is OK for you and also how to configure this as an scheduled task since I don't find the CELERY BEAT setting. |
We can always improve this task but it should be enough (for some definition) at this moment. It could be a good starting point. |
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.
So this method is close. Perhaps my suggestion is the best compromise between complexity and the most correct UX.
readthedocs/projects/tasks.py
Outdated
|
||
@app.task() | ||
def finish_inactive_builds(): | ||
delta = datetime.timedelta(hours=1, minutes=30) |
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.
Lets use a timedelta
of our base task timeout. For an example of how we add overhead to this, see:
https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/core/utils/__init__.py#L118-L128
There's a second part to this though, see below.
readthedocs/projects/tasks.py
Outdated
|
||
builds = Build.objects.filter(query)[:50] | ||
for build in builds: | ||
build.success = False |
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.
Let's check for the timeout date here, ie - if build.date + build.project.container_time_limit > now: continue
.
So the query will surface extra builds, but the custom timeout builds will be an exception to this case. They keep running through this loop until the condition above is true.
Does this seem reasonable?
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.
Yes. I think this is the easiest and more reasonable thing to do.
"I was overengeering it" as Eric said :)
readthedocs/projects/tasks.py
Outdated
'This build was terminated due to inactivity. If you ' | ||
'continue to encounter this error, file a support ' | ||
'request with and reference this build id ({0}).'.format(build.pk) | ||
) |
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 should be translated, though I'm not sure if we're equipped to actually translate this yet.
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.
Equipped? In what sense?
I don't understand how/where the translation are working yet.
This PR should be ready. Do I need to do something in particular to enable this task on celery beat? |
c15d774
to
a014493
Compare
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.
Looks good!
I think we'll want at least 3 test cases out of this:
- Legitimate build isn't marked finished early
- Build is custom time limit isn't marked finished early
- Build without custom time limit is marked finished
readthedocs/projects/tasks.py
Outdated
|
||
if build.project.container_time_limit: | ||
custom_delta = datetime.timedelta( | ||
minutes=int(build.project.container_time_limit)) |
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.
container_time_limit
is in seconds, so delta will need to be changed here
I added a test case for this escenario. I thought it was better to have 3 builds at the same time instead of one build per test. Let me know if you agree with me or want me to split them. I think it's easier to understand and also it's more close to the reality. |
Giddyup. The tests look great, I don't think it's important to split them up at all |
The only thing missing is setup the celery beat to effectively run this task
El 14 dic. 2017 3:59 p. m., "Anthony" <[email protected]> escribió:
… Merged #3312 <#3312>.
—
You are receiving this because you were assigned.
Reply to this email directly, view it on GitHub
<#3312 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAO7sF-eg9wRiH5T-5CALAU1bL2qOBC_ks5tAYwkgaJpZM4QpOay>
.
|
@agjohnson @ericholscher what do we need to do to enable this task to be ran by celery beat? |
This is an initial idea to solve these kind of issues and avoid user confusions:
celery beat
for periodic tasks so we need to define how this will be executed. Maybe it could be done as a django command and ran it by cron (depending how is the current setup at RTD servers)Here is an example on how this will look like