-
Notifications
You must be signed in to change notification settings - Fork 466
Add graceful max execution timeout. #425
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
Add graceful max execution timeout. #425
Conversation
Allows for running consumer up to certain time, which guarantees, that resources are cleaned up periodically and unobtrusively.
|
Using Moreover, for some reason invoking consumer with But most of all: I really value the fact, that tasks wouldn't be interrupted, even if the timeout hits during tasks' progress. And I don't think I'm the only one thinking this way. Thanks. |
README.md
Outdated
callback: upload_picture_service | ||
|
||
# 30 minutes | ||
graceful_max_execution_timeout: 1800 |
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 would rename those two options into something like:
consumers:
upload_picture:
graceful_max_execution:
timeout: 1800 # 30 minutes
exit_code: 10 # by default: 0
RabbitMq/Consumer.php
Outdated
$waitTimeout['timeoutType'] === self::TIMEOUT_TYPE_GRACEFUL_MAX_EXECUTION | ||
&& $waitTimeout['seconds'] < 1 | ||
) { | ||
return $this->getGracefulMaxExecutionTimeoutExitCode(); |
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.
No need to call method, just call variable directly.
Tests/RabbitMq/ConsumerTest.php
Outdated
->method('wait') | ||
->willThrowException(new AMQPTimeoutException()); | ||
|
||
$this->assertTrue(10 == $consumer->consume(1)); |
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.
assertSame()
should be used.
* Respect the idle timeout if it's set and if it's less than | ||
* the remaining allowed execution. | ||
*/ | ||
if ( |
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 if
could be opposite & the duplicated return
with self::TIMEOUT_TYPE_IDLE
could be removed then.
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'm sorry, but I disagree.
The code is not duplicated. This return statement is the same as the one a the end of the method, but the intention is different. The code after line 294 means: "do this, if Graceful Max Execution feature is not enabled". The code in the if statement starting on line 280 means: "Use Idle Timeout, if it's less than the computed remaining allowed execution". Currently, they are same in terms of code. But if tomorrow someone wanted to add some other crazy type of timeout, then they would modify the code after line 294. I bet they wouldn't think: "Ok, that other guy from Graceful whatever relied on the code after line 294 to return the Idle timeout". And then my code breaks.
Clear naming and no duplication are my prime principles. However I wouldn't trade "being proper" for "being pragmatic" here.
Please confirm, that you'd like me to remove the "duplication" despite what I said.
RabbitMq/Consumer.php
Outdated
/** | ||
* @param \DateTime|null $dateTime | ||
*/ | ||
public function setGracefulMaxExecutionDateTime($dateTime = null) |
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.
Type-hint is required here.
*/ | ||
public function setGracefulMaxExecutionDateTimeFromSecondsInTheFuture($secondsInTheFuture) | ||
{ | ||
$this->setGracefulMaxExecutionDateTime(new \DateTime("+{$secondsInTheFuture} seconds")); |
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.
You should add some validation to be sure you get seconds, maybe on configuration level?
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.
You mean validation, that I get an integer? Or that I get seconds and not minutes or hours?
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 added validation, that the value is integer on configuration level. Please let me know if this is enough.
Apply requested changes.
@stloyd Hi, I replied to your some of your code review comments. I'm not sure, whether you got a notification from github about that fact, so I thought I'd at-mention you as well. Thanks. |
@d-ph Sorry it took so long & thanks for your work! |
HI @d-ph for what I can see this doesn't apply to multiple consumers, right? |
Yeah, you're right, it's per consumer thing, which I guess is good and bad. Good, because it allows you to enable this feature per consumer basis (although I'm not sure why you wouldn't want to use my feature for all of the consumers). Bad, because it requires you to mention the same config for many (all?) consumers. What I personally do is I utilise YAML's hashmap merge feature to cut down on duplication like this: (...)
producers:
default:
connection: default
service_alias: default_rabbitmq_producer
consumers:
consumer1: &consumer_template
connection: default
queue_options: { name: 'consumer1' }
callback: 'my_app_bundle.queue.consumer.consumer1'
qos_options: { prefetch_size: 0, prefetch_count: 1, global: false }
graceful_max_execution:
timeout: '%rabbitmq.consumers_graceful_max_execution_timeout%'
exit_code: '%rabbitmq.consumers_graceful_max_execution_timeout_exit_code%'
consumer2:
<<: *consumer_template
queue_options: { name: 'consumer2' }
callback: 'my_app_bundle.queue.consumer.consumer2'
consumer3:
<<: *consumer_template
queue_options: { name: 'consumer3' }
callback: 'my_app_bundle.queue.consumer.consumer3' I highly recommend following this pattern. Hope this is what you asked about. Any questions - shoot. |
Wow, I wasn't aware of that yaml feature, thanks @d-ph, it will come handy for our consumers :p |
I wasn't aware of the I just realised, that there's an open PR with this addition already: #468 |
Allows for running consumer up to certain time, which guarantees, that resources are cleaned up periodically and unobtrusively (assuming, that workers are then restarted by something like
supervisord
).