Repository: celery/celery

Issue:
make REVOKES_MAX and REVOKE_EXPIRES configurable
Values of REVOKE_EXPIRES and REVOKES_MAX in worker/state.py  are hardcoded.

This should be configurable. Some of us really needed to change this.




Discussion:
any suggested changes on your mind? feel free send a PR
will try, thanks for pushing celery forward :)


I think this can be programmatically supported via adding `control` commands, similar to the following:

```python
from celery.worker.control import control_command

from celery.worker import state as worker_state

@control_command(
    args=[('n', int)],
    signature='[N={}]'.format(worker_state.REVOKES_MAX),  # <- used for help on the command-line.
)
def revokes_max(state, n=worker_state.REVOKES_MAX):
    if n != worker_state.revoked.maxlen:
        prev_max_len = worker_state.revoked.maxlen
        worker_state.revoked.maxlen = n
        if n < prev_max_len:
            worker_state.revoked.purge()
    return {'ok': 'updated revoked task max length.'}


@control_command(
    args=[('n', int)],
    signature='[N={}]'.format(worker_state.REVOKE_EXPIRES),  # <- used for help on the command-line.
)
def revokes_expires(state, n=worker_state.REVOKE_EXPIRES):
    if n != worker_state.revoked.expires:
        prev_expires = worker_state.revoked.expires
        worker_state.revoked.expires = n
        if n < prev_expires:
            worker_state.revoked.purge()
    return {'ok': 'updated revoked task expiration.'}
```

Of course, _not_ having to call control commands right after a deploy of workers comes up, and instead make it configurable like many other parameters in the conf, would be ideal. But, this is a stop-gap (and also allows it to be changed on the fly, which is at least minorly useful).

Resolving patch:
diff --git a/celery/worker/state.py b/celery/worker/state.py
--- a/celery/worker/state.py
+++ b/celery/worker/state.py
@@ -32,18 +32,18 @@
 }
 
 #: maximum number of revokes to keep in memory.
-REVOKES_MAX = 50000
+REVOKES_MAX = int(os.environ.get('CELERY_WORKER_REVOKES_MAX', 50000))
 
 #: maximum number of successful tasks to keep in memory.
-SUCCESSFUL_MAX = 1000
+SUCCESSFUL_MAX = int(os.environ.get('CELERY_WORKER_SUCCESSFUL_MAX', 1000))
 
 #: how many seconds a revoke will be active before
 #: being expired when the max limit has been exceeded.
-REVOKE_EXPIRES = 10800
+REVOKE_EXPIRES = float(os.environ.get('CELERY_WORKER_REVOKE_EXPIRES', 10800))
 
 #: how many seconds a successful task will be cached in memory
 #: before being expired when the max limit has been exceeded.
-SUCCESSFUL_EXPIRES = 10800
+SUCCESSFUL_EXPIRES = float(os.environ.get('CELERY_WORKER_SUCCESSFUL_EXPIRES', 10800))
 
 #: Mapping of reserved task_id->Request.
 requests = {}


Test patch:
diff --git a/t/unit/worker/test_state.py b/t/unit/worker/test_state.py
--- a/t/unit/worker/test_state.py
+++ b/t/unit/worker/test_state.py
@@ -1,4 +1,7 @@
+import os
 import pickle
+import sys
+from importlib import import_module
 from time import time
 from unittest.mock import Mock, patch
 
@@ -187,3 +190,32 @@ def test_ready(self, requests=[SimpleReq('foo'),
         for request in requests:
             state.task_ready(request)
         assert len(state.active_requests) == 0
+
+
+class test_state_configuration():
+
+    @staticmethod
+    def import_state():
+        with patch.dict(sys.modules):
+            del sys.modules['celery.worker.state']
+            return import_module('celery.worker.state')
+
+    @patch.dict(os.environ, {
+        'CELERY_WORKER_REVOKES_MAX': '50001',
+        'CELERY_WORKER_SUCCESSFUL_MAX': '1001',
+        'CELERY_WORKER_REVOKE_EXPIRES': '10801',
+        'CELERY_WORKER_SUCCESSFUL_EXPIRES': '10801',
+    })
+    def test_custom_configuration(self):
+        state = self.import_state()
+        assert state.REVOKES_MAX == 50001
+        assert state.SUCCESSFUL_MAX == 1001
+        assert state.REVOKE_EXPIRES == 10801
+        assert state.SUCCESSFUL_EXPIRES == 10801
+
+    def test_default_configuration(self):
+        state = self.import_state()
+        assert state.REVOKES_MAX == 50000
+        assert state.SUCCESSFUL_MAX == 1000
+        assert state.REVOKE_EXPIRES == 10800
+        assert state.SUCCESSFUL_EXPIRES == 10800