threadmill 0.7.1__tar.gz → 0.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {threadmill-0.7.1 → threadmill-0.7.2}/PKG-INFO +2 -1
  2. {threadmill-0.7.1 → threadmill-0.7.2}/pyproject.toml +1 -0
  3. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/_version.py +3 -3
  4. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/lua/acquire.lua +9 -4
  5. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/redis.py +4 -0
  6. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/management/commands/threadmill.py +1 -1
  7. {threadmill-0.7.1 → threadmill-0.7.2}/LICENSE +0 -0
  8. {threadmill-0.7.1 → threadmill-0.7.2}/README.md +0 -0
  9. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/__init__.py +0 -0
  10. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/apps.py +0 -0
  11. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/__init__.py +0 -0
  12. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/base.py +0 -0
  13. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/lua/acknowledge.lua +0 -0
  14. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/lua/mover.lua +0 -0
  15. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/backends/lua/reaper.lua +0 -0
  16. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/exceptions.py +0 -0
  17. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/executor.py +0 -0
  18. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/inspector/__init__.py +0 -0
  19. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/inspector/app.py +0 -0
  20. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/inspector/inspector.scss +0 -0
  21. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/inspector/screens.py +0 -0
  22. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/inspector/telemetry.py +0 -0
  23. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/management/__init__.py +0 -0
  24. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/management/commands/__init__.py +0 -0
  25. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/retry.py +0 -0
  26. {threadmill-0.7.1 → threadmill-0.7.2}/threadmill/signals.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: threadmill
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: The most reliable backend for Django's task framework.
5
5
  Keywords: Django,tasks,worker
6
6
  Author-email: Johannes Maron <johannes@maron.family>
@@ -18,6 +18,7 @@ Classifier: Operating System :: POSIX
18
18
  Classifier: Programming Language :: Python
19
19
  Classifier: Programming Language :: Python :: 3 :: Only
20
20
  Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3.15
21
22
  Classifier: Topic :: Communications :: Email
22
23
  Classifier: Topic :: Software Development
23
24
  Classifier: Topic :: Text Processing :: Markup :: Markdown
@@ -24,6 +24,7 @@ classifiers = [
24
24
  "Programming Language :: Python",
25
25
  "Programming Language :: Python :: 3 :: Only",
26
26
  "Programming Language :: Python :: 3.14",
27
+ "Programming Language :: Python :: 3.15",
27
28
  "Topic :: Communications :: Email",
28
29
  "Topic :: Software Development",
29
30
  "Topic :: Text Processing :: Markup :: Markdown",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.7.1'
22
- __version_tuple__ = version_tuple = (0, 7, 1)
21
+ __version__ = version = '0.7.2'
22
+ __version_tuple__ = version_tuple = (0, 7, 2)
23
23
 
24
- __commit_id__ = commit_id = 'gc60eae976'
24
+ __commit_id__ = commit_id = 'g560d3b3b5'
@@ -1,6 +1,7 @@
1
1
  -- Atomically pop the lowest-scored task from any of the given priority queues,
2
2
  -- update its JSON data with worker info, and move it directly to the running
3
- -- set. Iterates queues in key order and returns the first available task.
3
+ -- set. Scans the queues round-robin from ARGV[7] and returns the first available
4
+ -- task, so a backlogged queue cannot starve its neighbours.
4
5
  --
5
6
  -- KEYS[1..N] -- interleaved running keys and queue keys, one pair per queue:
6
7
  -- KEYS[1] = running set, KEYS[2] = queue set, KEYS[3] = running,
@@ -11,12 +12,16 @@
11
12
  -- ARGV[4] -- number of queue pairs (N/2)
12
13
  -- ARGV[5] -- worker name
13
14
  -- ARGV[6] -- lease TTL in milliseconds
15
+ -- ARGV[7] -- start_index; 0-based index of the queue pair to scan first, so
16
+ -- start_index 0 is the pair at KEYS[1] and KEYS[2]
14
17
  -- Returns: updated serialized data on success, nil if all queues are empty.
15
18
 
16
19
  local num_queues = tonumber(ARGV[4])
17
20
  local lease_ttl_ms = tonumber(ARGV[6])
18
- for i = 1, num_queues do
19
- local result = redis.call('ZPOPMIN', KEYS[i * 2])
21
+ local start_index = tonumber(ARGV[7])
22
+ for offset = 0, num_queues - 1 do
23
+ local queue_index = (start_index + offset) % num_queues + 1
24
+ local result = redis.call('ZPOPMIN', KEYS[queue_index * 2])
20
25
  if #result > 0 then
21
26
  local task_id = result[1]
22
27
  local data = redis.call('HGET', ARGV[3] .. task_id, 'data')
@@ -34,7 +39,7 @@ for i = 1, num_queues do
34
39
  table.insert(parsed.worker_ids, ARGV[5])
35
40
  local updated_data = cjson.encode(parsed)
36
41
  local deadline = tonumber(ARGV[1]) + lease_ttl_ms
37
- redis.call('ZADD', KEYS[i * 2 - 1], deadline, task_id)
42
+ redis.call('ZADD', KEYS[queue_index * 2 - 1], deadline, task_id)
38
43
  redis.call('HSET', ARGV[3] .. task_id, 'data', updated_data)
39
44
  return updated_data
40
45
  end
@@ -5,6 +5,7 @@ import dataclasses
5
5
  import datetime
6
6
  import logging
7
7
  import queue
8
+ import random
8
9
  import time
9
10
  import uuid
10
11
  from collections.abc import Generator, Sequence
@@ -162,6 +163,7 @@ class RedisTaskBackend(ThreadmillTaskBackend):
162
163
  "poll_max_interval", datetime.timedelta(seconds=1)
163
164
  )
164
165
  self._miss_count = 0
166
+ self._rotation_offset = random.randrange(len(self.queues)) # noqa: S311
165
167
  self._acquire_script = self.client.register_script(self.ACQUIRE_SCRIPT)
166
168
  self._acknowledge_script = self.client.register_script(self.ACKNOWLEDGE_SCRIPT)
167
169
 
@@ -275,9 +277,11 @@ class RedisTaskBackend(ThreadmillTaskBackend):
275
277
  str(len(queue_names)),
276
278
  worker,
277
279
  str(int(self.lease_ttl.total_seconds() * 1000)),
280
+ str(self._rotation_offset),
278
281
  ],
279
282
  ):
280
283
  self._miss_count = 0
284
+ self._rotation_offset = (self._rotation_offset + 1) % len(queue_names)
281
285
  return self.deserialize_task_result(data)
282
286
 
283
287
  try:
@@ -38,7 +38,7 @@ class WorkerCommand(DjangoBaseCommand):
38
38
  "--queues",
39
39
  nargs="+",
40
40
  default=[DEFAULT_TASK_QUEUE_NAME],
41
- help="Queue names to listen to and process tasks from.",
41
+ help="Queue names to listen to and process tasks from (round-robin).",
42
42
  )
43
43
  parser.add_argument(
44
44
  "-w",
File without changes
File without changes