tasktiger 0.19__tar.gz → 0.19.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.
- {tasktiger-0.19 → tasktiger-0.19.2}/PKG-INFO +30 -9
- tasktiger-0.19/tasktiger.egg-info/PKG-INFO → tasktiger-0.19.2/README.rst +26 -28
- {tasktiger-0.19 → tasktiger-0.19.2}/setup.py +4 -3
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/executor.py +58 -13
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/worker.py +2 -0
- tasktiger-0.19/README.rst → tasktiger-0.19.2/tasktiger.egg-info/PKG-INFO +49 -6
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_workers.py +59 -2
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/utils.py +4 -5
- {tasktiger-0.19 → tasktiger-0.19.2}/LICENSE +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/MANIFEST.in +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/pyproject.toml +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/setup.cfg +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/__init__.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/_internal.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/exceptions.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/flask_script.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/logging.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/lua/move_task.lua +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/lua/semaphore.lua +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/migrations.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/redis_scripts.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/redis_semaphore.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/retry.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/rollbar.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/runner.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/schedule.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/stats.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/task.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/tasktiger.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/test_helpers.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/timeouts.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/types.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger/utils.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger.egg-info/SOURCES.txt +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger.egg-info/dependency_links.txt +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger.egg-info/entry_points.txt +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger.egg-info/requires.txt +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tasktiger.egg-info/top_level.txt +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/__init__.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/config.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/conftest.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/tasks.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/tasks_periodic.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_base.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_context_manager.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_lazy_init.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_logging.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_migrations.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_periodic.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_queue_size.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_redis_scripts.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_semaphore.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_stats.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tests/test_task.py +0 -0
- {tasktiger-0.19 → tasktiger-0.19.2}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tasktiger
|
|
3
|
-
Version: 0.19
|
|
3
|
+
Version: 0.19.2
|
|
4
4
|
Summary: Python task queue
|
|
5
5
|
Home-page: http://github.com/closeio/tasktiger
|
|
6
6
|
License: MIT
|
|
@@ -12,9 +12,10 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
12
12
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
19
|
License-File: LICENSE
|
|
19
20
|
Requires-Dist: click
|
|
20
21
|
Requires-Dist: redis<5,>=3.3.0
|
|
@@ -40,13 +41,21 @@ TaskTiger
|
|
|
40
41
|
Features
|
|
41
42
|
--------
|
|
42
43
|
|
|
43
|
-
- Per-task fork
|
|
44
|
+
- Per-task fork or synchronous worker
|
|
44
45
|
|
|
45
|
-
TaskTiger forks a subprocess for each task, This comes with
|
|
46
|
-
Memory leaks caused by tasks are avoided since the
|
|
47
|
-
when the task is finished. A hard time limit can be
|
|
48
|
-
which the task is killed if it hasn't completed. To
|
|
49
|
-
necessary Python modules can be preloaded in the
|
|
46
|
+
By default, TaskTiger forks a subprocess for each task, This comes with
|
|
47
|
+
several benefits: Memory leaks caused by tasks are avoided since the
|
|
48
|
+
subprocess is terminated when the task is finished. A hard time limit can be
|
|
49
|
+
set for each task, after which the task is killed if it hasn't completed. To
|
|
50
|
+
ensure performance, any necessary Python modules can be preloaded in the
|
|
51
|
+
parent process.
|
|
52
|
+
|
|
53
|
+
TaskTiger also supports synchronous workers, which allows for better
|
|
54
|
+
performance due to no forking overhead, and tasks have the ability to reuse
|
|
55
|
+
network connections. To prevent memory leaks from accumulating, workers can
|
|
56
|
+
be set to shutdown after a certain amount of time, at which point a
|
|
57
|
+
supervisor can restart them. Workers also automatically exit on on hard
|
|
58
|
+
timeouts to prevent an inconsistent process state.
|
|
50
59
|
|
|
51
60
|
- Unique queues
|
|
52
61
|
|
|
@@ -574,6 +583,18 @@ Workers support the following options:
|
|
|
574
583
|
|
|
575
584
|
Store tracebacks with execution history (config defaults to ``True``).
|
|
576
585
|
|
|
586
|
+
- ``--executor``
|
|
587
|
+
|
|
588
|
+
Can be ``fork`` (default) or ``sync``. Whether to execute tasks in a separate
|
|
589
|
+
process via fork, or execute them synchronously in the same proces. See
|
|
590
|
+
"Features" section for the benefits of either approach.
|
|
591
|
+
|
|
592
|
+
- ``--exit-after``
|
|
593
|
+
|
|
594
|
+
Exit the worker after the time in minutes has elapsed. This is mainly useful
|
|
595
|
+
with the synchronous executor to prevent memory leaks from accumulating.
|
|
596
|
+
|
|
597
|
+
|
|
577
598
|
In some cases it is convenient to have a custom TaskTiger launch script. For
|
|
578
599
|
example, your application may have a ``manage.py`` command that sets up the
|
|
579
600
|
environment and you may want to launch TaskTiger workers using that script. To
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: tasktiger
|
|
3
|
-
Version: 0.19
|
|
4
|
-
Summary: Python task queue
|
|
5
|
-
Home-page: http://github.com/closeio/tasktiger
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Intended Audience :: Developers
|
|
8
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
-
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
|
-
Classifier: Operating System :: POSIX
|
|
11
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
-
Classifier: Programming Language :: Python
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Requires-Dist: click
|
|
20
|
-
Requires-Dist: redis<5,>=3.3.0
|
|
21
|
-
Requires-Dist: structlog
|
|
22
|
-
|
|
23
1
|
=========
|
|
24
2
|
TaskTiger
|
|
25
3
|
=========
|
|
@@ -40,13 +18,21 @@ TaskTiger
|
|
|
40
18
|
Features
|
|
41
19
|
--------
|
|
42
20
|
|
|
43
|
-
- Per-task fork
|
|
21
|
+
- Per-task fork or synchronous worker
|
|
22
|
+
|
|
23
|
+
By default, TaskTiger forks a subprocess for each task, This comes with
|
|
24
|
+
several benefits: Memory leaks caused by tasks are avoided since the
|
|
25
|
+
subprocess is terminated when the task is finished. A hard time limit can be
|
|
26
|
+
set for each task, after which the task is killed if it hasn't completed. To
|
|
27
|
+
ensure performance, any necessary Python modules can be preloaded in the
|
|
28
|
+
parent process.
|
|
44
29
|
|
|
45
|
-
TaskTiger
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
30
|
+
TaskTiger also supports synchronous workers, which allows for better
|
|
31
|
+
performance due to no forking overhead, and tasks have the ability to reuse
|
|
32
|
+
network connections. To prevent memory leaks from accumulating, workers can
|
|
33
|
+
be set to shutdown after a certain amount of time, at which point a
|
|
34
|
+
supervisor can restart them. Workers also automatically exit on on hard
|
|
35
|
+
timeouts to prevent an inconsistent process state.
|
|
50
36
|
|
|
51
37
|
- Unique queues
|
|
52
38
|
|
|
@@ -574,6 +560,18 @@ Workers support the following options:
|
|
|
574
560
|
|
|
575
561
|
Store tracebacks with execution history (config defaults to ``True``).
|
|
576
562
|
|
|
563
|
+
- ``--executor``
|
|
564
|
+
|
|
565
|
+
Can be ``fork`` (default) or ``sync``. Whether to execute tasks in a separate
|
|
566
|
+
process via fork, or execute them synchronously in the same proces. See
|
|
567
|
+
"Features" section for the benefits of either approach.
|
|
568
|
+
|
|
569
|
+
- ``--exit-after``
|
|
570
|
+
|
|
571
|
+
Exit the worker after the time in minutes has elapsed. This is mainly useful
|
|
572
|
+
with the synchronous executor to prevent memory leaks from accumulating.
|
|
573
|
+
|
|
574
|
+
|
|
577
575
|
In some cases it is convenient to have a custom TaskTiger launch script. For
|
|
578
576
|
example, your application may have a ``manage.py`` command that sets up the
|
|
579
577
|
environment and you may want to launch TaskTiger workers using that script. To
|
|
@@ -9,7 +9,7 @@ tests_require = install_requires + ["freezefrog", "pytest", "psutil"]
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="tasktiger",
|
|
12
|
-
version="0.19",
|
|
12
|
+
version="0.19.2",
|
|
13
13
|
url="http://github.com/closeio/tasktiger",
|
|
14
14
|
license="MIT",
|
|
15
15
|
description="Python task queue",
|
|
@@ -26,9 +26,10 @@ setup(
|
|
|
26
26
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
27
|
"Programming Language :: Python",
|
|
28
28
|
"Programming Language :: Python :: 3",
|
|
29
|
-
"Programming Language :: Python :: 3.6",
|
|
30
|
-
"Programming Language :: Python :: 3.7",
|
|
31
29
|
"Programming Language :: Python :: 3.8",
|
|
30
|
+
"Programming Language :: Python :: 3.9",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
32
33
|
],
|
|
33
34
|
packages=["tasktiger"],
|
|
34
35
|
package_data={"tasktiger": ["lua/*.lua"]},
|
|
@@ -63,6 +63,25 @@ class Executor:
|
|
|
63
63
|
self.connection = worker.connection
|
|
64
64
|
self.config = worker.config
|
|
65
65
|
|
|
66
|
+
def heartbeat(
|
|
67
|
+
self,
|
|
68
|
+
queue: str,
|
|
69
|
+
task_ids: Collection[str],
|
|
70
|
+
log: BoundLogger,
|
|
71
|
+
locks: Collection[Lock],
|
|
72
|
+
queue_lock: Optional[Semaphore],
|
|
73
|
+
) -> None:
|
|
74
|
+
self.worker.heartbeat(queue, task_ids)
|
|
75
|
+
for lock in locks:
|
|
76
|
+
try:
|
|
77
|
+
lock.reacquire()
|
|
78
|
+
except LockError:
|
|
79
|
+
log.warning("could not reacquire lock", lock=lock.name)
|
|
80
|
+
if queue_lock:
|
|
81
|
+
acquired, current_locks = queue_lock.renew()
|
|
82
|
+
if not acquired:
|
|
83
|
+
log.debug("queue lock renew failure")
|
|
84
|
+
|
|
66
85
|
def execute(
|
|
67
86
|
self,
|
|
68
87
|
queue: str,
|
|
@@ -351,18 +370,7 @@ class ForkExecutor(Executor):
|
|
|
351
370
|
break
|
|
352
371
|
|
|
353
372
|
try:
|
|
354
|
-
self.
|
|
355
|
-
for lock in locks:
|
|
356
|
-
try:
|
|
357
|
-
lock.reacquire()
|
|
358
|
-
except LockError:
|
|
359
|
-
log.warning(
|
|
360
|
-
"could not reacquire lock", lock=lock.name
|
|
361
|
-
)
|
|
362
|
-
if queue_lock:
|
|
363
|
-
acquired, current_locks = queue_lock.renew()
|
|
364
|
-
if not acquired:
|
|
365
|
-
log.debug("queue lock renew failure")
|
|
373
|
+
self.heartbeat(queue, all_task_ids, log, locks, queue_lock)
|
|
366
374
|
except OSError as e:
|
|
367
375
|
# EINTR happens if the task completed. Since we're just
|
|
368
376
|
# renewing locks/heartbeat it's okay if we get interrupted.
|
|
@@ -386,6 +394,21 @@ class SyncExecutor(Executor):
|
|
|
386
394
|
|
|
387
395
|
exit_worker_on_job_timeout = True
|
|
388
396
|
|
|
397
|
+
def _periodic_heartbeat(
|
|
398
|
+
self,
|
|
399
|
+
queue: str,
|
|
400
|
+
task_ids: Collection[str],
|
|
401
|
+
log: BoundLogger,
|
|
402
|
+
locks: Collection[Lock],
|
|
403
|
+
queue_lock: Optional[Semaphore],
|
|
404
|
+
stop_event: threading.Event,
|
|
405
|
+
) -> None:
|
|
406
|
+
while not stop_event.wait(self.config["ACTIVE_TASK_UPDATE_TIMER"]):
|
|
407
|
+
try:
|
|
408
|
+
self.heartbeat(queue, task_ids, log, locks, queue_lock)
|
|
409
|
+
except Exception:
|
|
410
|
+
log.exception("task heartbeat failed")
|
|
411
|
+
|
|
389
412
|
def execute(
|
|
390
413
|
self,
|
|
391
414
|
queue: str,
|
|
@@ -394,5 +417,27 @@ class SyncExecutor(Executor):
|
|
|
394
417
|
locks: Collection[Lock],
|
|
395
418
|
queue_lock: Optional[Semaphore],
|
|
396
419
|
) -> bool:
|
|
420
|
+
# Run heartbeat thread.
|
|
421
|
+
all_task_ids = {task.id for task in tasks}
|
|
422
|
+
stop_event = threading.Event()
|
|
423
|
+
heartbeat_thread = threading.Thread(
|
|
424
|
+
target=self._periodic_heartbeat,
|
|
425
|
+
kwargs={
|
|
426
|
+
"queue": queue,
|
|
427
|
+
"task_ids": all_task_ids,
|
|
428
|
+
"log": log,
|
|
429
|
+
"locks": locks,
|
|
430
|
+
"queue_lock": queue_lock,
|
|
431
|
+
"stop_event": stop_event,
|
|
432
|
+
},
|
|
433
|
+
)
|
|
434
|
+
heartbeat_thread.start()
|
|
435
|
+
|
|
397
436
|
# Run the tasks.
|
|
398
|
-
|
|
437
|
+
result = self.execute_tasks(tasks, log)
|
|
438
|
+
|
|
439
|
+
# Stop the heartbeat thread.
|
|
440
|
+
stop_event.set()
|
|
441
|
+
heartbeat_thread.join()
|
|
442
|
+
|
|
443
|
+
return result
|
|
@@ -674,6 +674,8 @@ class Worker:
|
|
|
674
674
|
lock = self.connection.lock(
|
|
675
675
|
self._key("lockv2", lock_id),
|
|
676
676
|
timeout=self.config["ACTIVE_TASK_UPDATE_TIMEOUT"],
|
|
677
|
+
# Sync worker uses a thread to renew the lock.
|
|
678
|
+
thread_local=False,
|
|
677
679
|
)
|
|
678
680
|
if not lock.acquire(blocking=False):
|
|
679
681
|
log.info("could not acquire lock", task_id=task.id)
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: tasktiger
|
|
3
|
+
Version: 0.19.2
|
|
4
|
+
Summary: Python task queue
|
|
5
|
+
Home-page: http://github.com/closeio/tasktiger
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
10
|
+
Classifier: Operating System :: POSIX
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: click
|
|
21
|
+
Requires-Dist: redis<5,>=3.3.0
|
|
22
|
+
Requires-Dist: structlog
|
|
23
|
+
|
|
1
24
|
=========
|
|
2
25
|
TaskTiger
|
|
3
26
|
=========
|
|
@@ -18,13 +41,21 @@ TaskTiger
|
|
|
18
41
|
Features
|
|
19
42
|
--------
|
|
20
43
|
|
|
21
|
-
- Per-task fork
|
|
44
|
+
- Per-task fork or synchronous worker
|
|
45
|
+
|
|
46
|
+
By default, TaskTiger forks a subprocess for each task, This comes with
|
|
47
|
+
several benefits: Memory leaks caused by tasks are avoided since the
|
|
48
|
+
subprocess is terminated when the task is finished. A hard time limit can be
|
|
49
|
+
set for each task, after which the task is killed if it hasn't completed. To
|
|
50
|
+
ensure performance, any necessary Python modules can be preloaded in the
|
|
51
|
+
parent process.
|
|
22
52
|
|
|
23
|
-
TaskTiger
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
53
|
+
TaskTiger also supports synchronous workers, which allows for better
|
|
54
|
+
performance due to no forking overhead, and tasks have the ability to reuse
|
|
55
|
+
network connections. To prevent memory leaks from accumulating, workers can
|
|
56
|
+
be set to shutdown after a certain amount of time, at which point a
|
|
57
|
+
supervisor can restart them. Workers also automatically exit on on hard
|
|
58
|
+
timeouts to prevent an inconsistent process state.
|
|
28
59
|
|
|
29
60
|
- Unique queues
|
|
30
61
|
|
|
@@ -552,6 +583,18 @@ Workers support the following options:
|
|
|
552
583
|
|
|
553
584
|
Store tracebacks with execution history (config defaults to ``True``).
|
|
554
585
|
|
|
586
|
+
- ``--executor``
|
|
587
|
+
|
|
588
|
+
Can be ``fork`` (default) or ``sync``. Whether to execute tasks in a separate
|
|
589
|
+
process via fork, or execute them synchronously in the same proces. See
|
|
590
|
+
"Features" section for the benefits of either approach.
|
|
591
|
+
|
|
592
|
+
- ``--exit-after``
|
|
593
|
+
|
|
594
|
+
Exit the worker after the time in minutes has elapsed. This is mainly useful
|
|
595
|
+
with the synchronous executor to prevent memory leaks from accumulating.
|
|
596
|
+
|
|
597
|
+
|
|
555
598
|
In some cases it is convenient to have a custom TaskTiger launch script. For
|
|
556
599
|
example, your application may have a ``manage.py`` command that sets up the
|
|
557
600
|
environment and you may want to launch TaskTiger workers using that script. To
|
|
@@ -8,13 +8,17 @@ import pytest
|
|
|
8
8
|
from freezefrog import FreezeTime
|
|
9
9
|
|
|
10
10
|
from tasktiger import Task, Worker
|
|
11
|
+
from tasktiger._internal import ACTIVE
|
|
11
12
|
from tasktiger.executor import SyncExecutor
|
|
13
|
+
from tasktiger.worker import LOCK_REDIS_KEY
|
|
12
14
|
|
|
15
|
+
from .config import DELAY
|
|
13
16
|
from .tasks import (
|
|
14
17
|
exception_task,
|
|
15
18
|
long_task_killed,
|
|
16
19
|
long_task_ok,
|
|
17
20
|
simple_task,
|
|
21
|
+
sleep_task,
|
|
18
22
|
wait_for_long_task,
|
|
19
23
|
)
|
|
20
24
|
from .test_base import BaseTestCase
|
|
@@ -35,10 +39,12 @@ class TestMaxWorkers(BaseTestCase):
|
|
|
35
39
|
|
|
36
40
|
# Start two workers and wait until they start processing.
|
|
37
41
|
worker1 = Process(
|
|
38
|
-
target=external_worker,
|
|
42
|
+
target=external_worker,
|
|
43
|
+
kwargs={"worker_kwargs": {"max_workers_per_queue": 2}},
|
|
39
44
|
)
|
|
40
45
|
worker2 = Process(
|
|
41
|
-
target=external_worker,
|
|
46
|
+
target=external_worker,
|
|
47
|
+
kwargs={"worker_kwargs": {"max_workers_per_queue": 2}},
|
|
42
48
|
)
|
|
43
49
|
worker1.start()
|
|
44
50
|
worker2.start()
|
|
@@ -181,3 +187,54 @@ class TestSyncExecutorWorker:
|
|
|
181
187
|
with pytest.raises(SystemExit):
|
|
182
188
|
worker.run(once=True, force_once=True)
|
|
183
189
|
ensure_queues(error={"default": 1})
|
|
190
|
+
|
|
191
|
+
def test_heartbeat(self, tiger):
|
|
192
|
+
# Test both task heartbeat and lock renewal.
|
|
193
|
+
# We set unique=True so the task ID matches the lock key.
|
|
194
|
+
task = Task(tiger, sleep_task, lock=True, unique=True)
|
|
195
|
+
task.delay()
|
|
196
|
+
|
|
197
|
+
# Start a worker and wait until it starts processing.
|
|
198
|
+
worker = Process(
|
|
199
|
+
target=external_worker,
|
|
200
|
+
kwargs={
|
|
201
|
+
"patch_config": {"ACTIVE_TASK_UPDATE_TIMER": DELAY / 2},
|
|
202
|
+
"worker_kwargs": {
|
|
203
|
+
# Test queue lock.
|
|
204
|
+
"max_workers_per_queue": 1,
|
|
205
|
+
"executor_class": SyncExecutor,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
worker.start()
|
|
210
|
+
|
|
211
|
+
time.sleep(DELAY)
|
|
212
|
+
|
|
213
|
+
queue_key = tiger._key(ACTIVE, "default")
|
|
214
|
+
queue_lock_key = tiger._key(LOCK_REDIS_KEY, "default")
|
|
215
|
+
task_lock_key = tiger._key("lockv2", task.id)
|
|
216
|
+
|
|
217
|
+
conn = tiger.connection
|
|
218
|
+
|
|
219
|
+
heartbeat_1 = conn.zscore(queue_key, task.id)
|
|
220
|
+
queue_lock_1 = conn.zrange(queue_lock_key, 0, -1, withscores=True)[0][
|
|
221
|
+
1
|
|
222
|
+
]
|
|
223
|
+
task_lock_1 = conn.pttl(task_lock_key)
|
|
224
|
+
|
|
225
|
+
time.sleep(DELAY / 2)
|
|
226
|
+
|
|
227
|
+
heartbeat_2 = conn.zscore(queue_key, task.id)
|
|
228
|
+
queue_lock_2 = conn.zrange(queue_lock_key, 0, -1, withscores=True)[0][
|
|
229
|
+
1
|
|
230
|
+
]
|
|
231
|
+
task_lock_2 = conn.pttl(task_lock_key)
|
|
232
|
+
|
|
233
|
+
assert heartbeat_2 > heartbeat_1 > 0
|
|
234
|
+
assert queue_lock_2 > queue_lock_1 > 0
|
|
235
|
+
|
|
236
|
+
# Active task update timeout is 2 * DELAY and we renew every DELAY / 2.
|
|
237
|
+
assert task_lock_1 > DELAY
|
|
238
|
+
assert task_lock_2 > DELAY
|
|
239
|
+
|
|
240
|
+
worker.kill()
|
|
@@ -70,7 +70,7 @@ def get_tiger():
|
|
|
70
70
|
return tiger
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def external_worker(n=None, patch_config=None,
|
|
73
|
+
def external_worker(n=None, patch_config=None, worker_kwargs=None):
|
|
74
74
|
"""
|
|
75
75
|
Runs a worker. To be used with multiprocessing.Pool.map.
|
|
76
76
|
"""
|
|
@@ -79,12 +79,11 @@ def external_worker(n=None, patch_config=None, max_workers_per_queue=None):
|
|
|
79
79
|
if patch_config:
|
|
80
80
|
tiger.config.update(patch_config)
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
if worker_kwargs is None:
|
|
83
|
+
worker_kwargs = {}
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
worker.max_workers_per_queue = max_workers_per_queue
|
|
85
|
+
Worker(tiger, **worker_kwargs).run(once=True, force_once=True)
|
|
86
86
|
|
|
87
|
-
worker.run(once=True, force_once=True)
|
|
88
87
|
tiger.connection.close()
|
|
89
88
|
|
|
90
89
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|