<h1 dir="auto">Checklist</h1>

<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have verified that the issue exists against the <code class="notranslate">master</code> branch of Celery.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> This has already been asked to the <a href="https://groups.google.com/forum/#!forum/celery-users" rel="nofollow">discussion group</a> first.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have read the relevant section in the<br>
<a href="http://docs.celeryproject.org/en/latest/contributing.html#other-bugs" rel="nofollow">contribution guide</a><br>
on reporting bugs.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/celery/celery/issues?q=is%3Aissue+label%3A%22Issue+Type%3A+Bug+Report%22+-label%3A%22Category%3A+Documentation%22">issues list</a><br>
for similar or identical bug reports.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/celery/celery/pulls?q=is%3Apr+label%3A%22PR+Type%3A+Bugfix%22+-label%3A%22Category%3A+Documentation%22">pull requests list</a><br>
for existing proposed fixes.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/celery/celery/commits/master">commit log</a><br>
to find out if the bug was already fixed in the master branch.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have included all related issues and possible duplicate issues<br>
in this issue (If there are none, check this box anyway).</li>
</ul>
<h2 dir="auto">Mandatory Debugging Information</h2>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have included the output of <code class="notranslate">celery -A proj report</code> in the issue.<br>
(if you are not able to do this, then at least specify the Celery<br>
version affected).</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have verified that the issue exists against the <code class="notranslate">master</code> branch of Celery.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have included the contents of <code class="notranslate">pip freeze</code> in the issue.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have included all the versions of all the external dependencies required<br>
to reproduce this bug.</li>
</ul>
<h2 dir="auto">Optional Debugging Information</h2>

<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have tried reproducing the issue on more than one Python version<br>
and/or implementation.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have tried reproducing the issue on more than one message broker and/or<br>
result backend.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have tried reproducing the issue on more than one version of the message<br>
broker and/or result backend.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have tried reproducing the issue on more than one operating system.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have tried reproducing the issue on more than one workers pool.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have tried reproducing the issue with autoscaling, retries,<br>
ETA/Countdown &amp; rate limits disabled.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have tried reproducing the issue after downgrading<br>
and/or upgrading Celery and its dependencies.</li>
</ul>
<h2 dir="auto">Related Issues and Possible Duplicates</h2>

<h4 dir="auto">Related Issues</h4>
<ul dir="auto">
<li>None</li>
</ul>
<h4 dir="auto">Possible Duplicates</h4>
<ul dir="auto">
<li>None</li>
</ul>
<h2 dir="auto">Environment &amp; Settings</h2>

<p dir="auto"><strong>Celery version</strong>: 4.2.2 (windowlicker)</p>

<details>
<summary><b><code class="notranslate">celery report</code> Output:</b></summary>
<p dir="auto">
BROKER_URL: 'redis://127.0.0.1:6379/2'
CELERYBEAT_SCHEDULE: {
 }
CELERY_ACCEPT_CONTENT: ['json']
CELERY_ENABLE_UTC: True
CELERY_QUEUES: 
    (  -&gt; celery&gt;,
   -&gt; campaigns&gt;)
CELERY_RESULT_SERIALIZER: 'json'
CELERY_ROUTES: 
    ('campaigns.task_routers.CampaignRouter',)
CELERY_TASK_SERIALIZER: 'json'
</p>
</details>
<h1 dir="auto">Steps to Reproduce</h1>
<h2 dir="auto">Required Dependencies</h2>

<ul dir="auto">
<li><strong>Minimal Python Version</strong>: 3.7</li>
<li><strong>Minimal Celery Version</strong>: 4.2.2 vs. 3.1.23</li>
<li><strong>Minimal Kombu Version</strong>: 4.3.0 vs. 3.0.35</li>
<li><strong>Minimal Broker Version</strong>: N/A or Unknown</li>
<li><strong>Minimal Result Backend Version</strong>: N/A or Unknown</li>
<li><strong>Minimal OS and/or Kernel Version</strong>: N/A or Unknown</li>
<li><strong>Minimal Broker Client Version</strong>: N/A or Unknown</li>
<li><strong>Minimal Result Backend Client Version</strong>: N/A or Unknown</li>
</ul>
<h3 dir="auto">Python Packages</h3>

<details>
<summary><b><code class="notranslate">pip freeze</code> Output:</b></summary>
<p dir="auto">
amqp==2.5.2
billiard==3.5.0.5
celery==4.2.2
Django==1.8.19
kombu==4.3.0
</p>
</details>
<h3 dir="auto">Other Dependencies</h3>

<details>
<p dir="auto">
N/A
</p>
</details>
<h2 dir="auto">Minimally Reproducible Test Case</h2>

<details>
<p dir="auto">
</p><ol dir="auto">
<li>
<p dir="auto">Create a minimum Django app that supports celery on python 3.7 (I've used Django==1.8.19)</p>
</li>
<li>
<p dir="auto">Create a task_router for example: CampaignRouter that returns a dictionary:</p>
</li>
</ol>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="class CampaignRouter(object):
    def route_for_task(self, task, args=None, kwargs=None):
        if task.startswith('campaigns.tasks.'):
            return {
                'queue': 'campaigns',
                'routing_key': 'campaigns',
                'time_limit': 30,  # 30sec  &lt;&lt;--- this is the problem
            }
        return None"><pre class="notranslate"><code class="notranslate">class CampaignRouter(object):
    def route_for_task(self, task, args=None, kwargs=None):
        if task.startswith('campaigns.tasks.'):
            return {
                'queue': 'campaigns',
                'routing_key': 'campaigns',
                'time_limit': 30,  # 30sec  &lt;&lt;--- this is the problem
            }
        return None
</code></pre></div>
<ol start="3" dir="auto">
<li>Create a task that does stuff for more then 5sec (you are going to pass --time-limit=5 in the command line) for example a 600sec sleep and will be routed by your route:</li>
</ol>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="@app.task(bind=True)
def my_task(self):
    time.sleep(1000 * 10)   # 10sec
    return 1+1"><pre class="notranslate"><code class="notranslate">@app.task(bind=True)
def my_task(self):
    time.sleep(1000 * 10)   # 10sec
    return 1+1
</code></pre></div>
<ol start="4" dir="auto">
<li>Run your django app</li>
<li>Run celery with the --time-limit=5:<br>
<code class="notranslate">celery worker -B -A sample_proj --loglevel=DEBUG --time-limit 5</code></li>
<li>execute the task</li>
<li>check if task run into a TimeLimitExceeded error after 5sec and cry:</li>
</ol>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    raise TimeLimitExceeded(job._timeout)
billiard.exceptions.TimeLimitExceeded: TimeLimitExceeded(5,)
[2020-04-02 09:37:05,183: ERROR/MainProcess] Hard time limit (5.0s) exceeded for "><pre class="notranslate"><code class="notranslate">    raise TimeLimitExceeded(job._timeout)
billiard.exceptions.TimeLimitExceeded: TimeLimitExceeded(5,)
[2020-04-02 09:37:05,183: ERROR/MainProcess] Hard time limit (5.0s) exceeded for 
</code></pre></div>
<p dir="auto"></p>
</details>
<h1 dir="auto">Expected Behavior</h1>

<p dir="auto">A task ran through the route should have the "time_limit" from the route's dictionary (30sec).</p>
<h1 dir="auto">Actual Behavior</h1>
<p dir="auto">I've included it in the TestCase scenario. The actual behevior is that the task ends after 5sec (the time passed in the command line).</p>

<p dir="auto">So basically I see that the interface for creating a message in <code class="notranslate">celery.app.base.send_task</code> has changed between 3.1.23 and 4.2.2. In Celery 3.1.23 you were able to pass the 'time_limit' through your route so each task that runs through it will have that specific time_limit (hard time limit in this case). That was done thanks to the **options argument.</p>
<p dir="auto">Celery 3.1.23 code (<code class="notranslate">celery.app.base.send_task</code> from line 345):</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="        options = router.route(options, name, args, kwargs)
        if connection:
            producer = self.amqp.TaskProducer(connection)
        with self.producer_or_acquire(producer) as P:
            self.backend.on_task_call(P, task_id)
            task_id = P.publish_task(
                name, args, kwargs, countdown=countdown, eta=eta,
                task_id=task_id, expires=expires,
                callbacks=maybe_list(link), errbacks=maybe_list(link_error),
                reply_to=reply_to or self.oid, **options
            )"><pre class="notranslate"><code class="notranslate">        options = router.route(options, name, args, kwargs)
        if connection:
            producer = self.amqp.TaskProducer(connection)
        with self.producer_or_acquire(producer) as P:
            self.backend.on_task_call(P, task_id)
            task_id = P.publish_task(
                name, args, kwargs, countdown=countdown, eta=eta,
                task_id=task_id, expires=expires,
                callbacks=maybe_list(link), errbacks=maybe_list(link_error),
                reply_to=reply_to or self.oid, **options
            )
</code></pre></div>
<p dir="auto">But in Celery 4 this isn't possible because the time_limit is pass from the send_task arguments to the amqp.create_task_message() and the "time_limit" from the route isn't even considered.</p>
<p dir="auto">Celery 4.2.2 code (<code class="notranslate">celery.app.base.send_task</code> from line 716):</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="        options = router.route(
            options, route_name or name, args, kwargs, task_type)

        if not root_id or not parent_id:
            parent = self.current_worker_task
            if parent:
                if not root_id:
                    root_id = parent.request.root_id or parent.request.id
                if not parent_id:
                    parent_id = parent.request.id

        message = amqp.create_task_message(
            task_id, name, args, kwargs, countdown, eta, group_id,
            expires, retries, chord,
            maybe_list(link), maybe_list(link_error),
            reply_to or self.oid, time_limit, soft_time_limit,
            self.conf.task_send_sent_event,
            root_id, parent_id, shadow, chain,
            argsrepr=options.get('argsrepr'),
            kwargsrepr=options.get('kwargsrepr'),
        )"><pre class="notranslate"><code class="notranslate">        options = router.route(
            options, route_name or name, args, kwargs, task_type)

        if not root_id or not parent_id:
            parent = self.current_worker_task
            if parent:
                if not root_id:
                    root_id = parent.request.root_id or parent.request.id
                if not parent_id:
                    parent_id = parent.request.id

        message = amqp.create_task_message(
            task_id, name, args, kwargs, countdown, eta, group_id,
            expires, retries, chord,
            maybe_list(link), maybe_list(link_error),
            reply_to or self.oid, time_limit, soft_time_limit,
            self.conf.task_send_sent_event,
            root_id, parent_id, shadow, chain,
            argsrepr=options.get('argsrepr'),
            kwargsrepr=options.get('kwargsrepr'),
        )
</code></pre></div>
<p dir="auto">This is in my opinion a big (not huge) regression because let's see this scenario of a existing project:</p>
<p dir="auto">10 django apps<br>
each app has 10tasks<br>
your 'slow app' has 20tasks</p>
<p dir="auto">If the project used celery 3.1.23 it just needed to create a route and set a key in the returned dictionary to like this: <code class="notranslate">{(other_keys), 'time_limit': 1000}</code>. But if the project decides to use the newest celery it need to add time_limit parameter to each task decorator for the slow app which will end up changing 20 places instead of handling this through the route. And what if we consider to add a new queue that will be for 'medium apps' that should have a smaller time_limit the the queue used be our 'slow app' but a bigger one the the default queue? We need to to the same job - upgrade each decorator with a specific time_limit.</p>
<p dir="auto">This sums up to that each task can have it's own time_limit OR the global time_limit. There is no 'golden mean' which was provided in 3.1.23 thanks to routes.</p>
<p dir="auto">How can this be fixed?<br>
My suggestion is to consider the time_limits params from a route. To get this done the keywords can even have the same names as the celery_configs (for convention) like: <a href="https://docs.celeryproject.org/en/stable/userguide/workers.html" rel="nofollow">task_time_limit / task_soft_time_limit</a></p>
<h1 dir="auto">Checklist</h1>

<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/celery/celery/issues?utf8=%E2%9C%93&amp;q=is%3Aissue+label%3A%22Category%3A+Documentation%22+">issues list</a><br>
for similar or identical bug reports.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/celery/celery/pulls?q=is%3Apr+label%3A%22Category%3A+Documentation%22">pull requests list</a><br>
for existing proposed fixes.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/celery/celery/commits/master">commit log</a><br>
to find out if the bug was already fixed in the master branch.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have included all related issues and possible duplicate issues in this issue<br>
(If there are none, check this box anyway).</li>
</ul>
<h2 dir="auto">Related Issues and Possible Duplicates</h2>

<h4 dir="auto">Related Issues</h4>
<ul dir="auto">
<li>None</li>
</ul>
<h4 dir="auto">Possible Duplicates</h4>
<ul dir="auto">
<li>None</li>
</ul>
<h1 dir="auto">Description</h1>

<p dir="auto">When starting workers like <a href="https://docs.celeryproject.org/en/stable/userguide/workers.html#starting-the-worker" rel="nofollow">some examples in the documentation</a> that uses lowercase <code class="notranslate">info</code> as argument to loglevel I get this error: <code class="notranslate">Error: Invalid value for '-l' / '--loglevel': invalid choice: info. (choose from DEBUG, INFO, WARNING, ERROR, CRITICAL, FATAL)</code>. I assume this is because of the change to Click in 5.0.</p>
<h1 dir="auto">Suggestions</h1>

<p dir="auto">Review documentation for instances of lowercase loglevel being passed and update them to use uppercase.</p>