jettask 0.2.23__py3-none-any.whl → 0.2.24__py3-none-any.whl
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.
- jettask/__init__.py +2 -0
- jettask/cli.py +12 -8
- jettask/config/lua_scripts.py +37 -0
- jettask/config/nacos_config.py +1 -1
- jettask/core/app.py +313 -340
- jettask/core/container.py +4 -4
- jettask/{persistence → core}/namespace.py +93 -27
- jettask/core/task.py +16 -9
- jettask/core/unified_manager_base.py +136 -26
- jettask/db/__init__.py +67 -0
- jettask/db/base.py +137 -0
- jettask/{utils/db_connector.py → db/connector.py} +130 -26
- jettask/db/models/__init__.py +16 -0
- jettask/db/models/scheduled_task.py +196 -0
- jettask/db/models/task.py +77 -0
- jettask/db/models/task_run.py +85 -0
- jettask/executor/__init__.py +0 -15
- jettask/executor/core.py +76 -31
- jettask/executor/process_entry.py +29 -114
- jettask/executor/task_executor.py +4 -0
- jettask/messaging/event_pool.py +928 -685
- jettask/messaging/scanner.py +30 -0
- jettask/persistence/__init__.py +28 -103
- jettask/persistence/buffer.py +170 -0
- jettask/persistence/consumer.py +330 -249
- jettask/persistence/manager.py +304 -0
- jettask/persistence/persistence.py +391 -0
- jettask/scheduler/__init__.py +15 -3
- jettask/scheduler/{task_crud.py → database.py} +61 -57
- jettask/scheduler/loader.py +2 -2
- jettask/scheduler/{scheduler_coordinator.py → manager.py} +23 -6
- jettask/scheduler/models.py +14 -10
- jettask/scheduler/schedule.py +166 -0
- jettask/scheduler/scheduler.py +12 -11
- jettask/schemas/__init__.py +50 -1
- jettask/schemas/backlog.py +43 -6
- jettask/schemas/namespace.py +70 -19
- jettask/schemas/queue.py +19 -3
- jettask/schemas/responses.py +493 -0
- jettask/task/__init__.py +0 -2
- jettask/task/router.py +3 -0
- jettask/test_connection_monitor.py +1 -1
- jettask/utils/__init__.py +7 -5
- jettask/utils/db_init.py +8 -4
- jettask/utils/namespace_dep.py +167 -0
- jettask/utils/queue_matcher.py +186 -0
- jettask/utils/rate_limit/concurrency_limiter.py +7 -1
- jettask/utils/stream_backlog.py +1 -1
- jettask/webui/__init__.py +0 -1
- jettask/webui/api/__init__.py +4 -4
- jettask/webui/api/alerts.py +806 -71
- jettask/webui/api/example_refactored.py +400 -0
- jettask/webui/api/namespaces.py +390 -45
- jettask/webui/api/overview.py +300 -54
- jettask/webui/api/queues.py +971 -267
- jettask/webui/api/scheduled.py +1249 -56
- jettask/webui/api/settings.py +129 -7
- jettask/webui/api/workers.py +442 -0
- jettask/webui/app.py +46 -2329
- jettask/webui/middleware/__init__.py +6 -0
- jettask/webui/middleware/namespace_middleware.py +135 -0
- jettask/webui/services/__init__.py +146 -0
- jettask/webui/services/heartbeat_service.py +251 -0
- jettask/webui/services/overview_service.py +60 -51
- jettask/webui/services/queue_monitor_service.py +426 -0
- jettask/webui/services/redis_monitor_service.py +87 -0
- jettask/webui/services/settings_service.py +174 -111
- jettask/webui/services/task_monitor_service.py +222 -0
- jettask/webui/services/timeline_pg_service.py +452 -0
- jettask/webui/services/timeline_service.py +189 -0
- jettask/webui/services/worker_monitor_service.py +467 -0
- jettask/webui/utils/__init__.py +11 -0
- jettask/webui/utils/time_utils.py +122 -0
- jettask/worker/lifecycle.py +8 -2
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/METADATA +1 -1
- jettask-0.2.24.dist-info/RECORD +142 -0
- jettask/executor/executor.py +0 -338
- jettask/persistence/backlog_monitor.py +0 -567
- jettask/persistence/base.py +0 -2334
- jettask/persistence/db_manager.py +0 -516
- jettask/persistence/maintenance.py +0 -81
- jettask/persistence/message_consumer.py +0 -259
- jettask/persistence/models.py +0 -49
- jettask/persistence/offline_recovery.py +0 -196
- jettask/persistence/queue_discovery.py +0 -215
- jettask/persistence/task_persistence.py +0 -218
- jettask/persistence/task_updater.py +0 -583
- jettask/scheduler/add_execution_count.sql +0 -11
- jettask/scheduler/add_priority_field.sql +0 -26
- jettask/scheduler/add_scheduler_id.sql +0 -25
- jettask/scheduler/add_scheduler_id_index.sql +0 -10
- jettask/scheduler/make_scheduler_id_required.sql +0 -28
- jettask/scheduler/migrate_interval_seconds.sql +0 -9
- jettask/scheduler/performance_optimization.sql +0 -45
- jettask/scheduler/run_scheduler.py +0 -186
- jettask/scheduler/schema.sql +0 -84
- jettask/task/task_executor.py +0 -318
- jettask/webui/api/analytics.py +0 -323
- jettask/webui/config.py +0 -90
- jettask/webui/models/__init__.py +0 -3
- jettask/webui/models/namespace.py +0 -63
- jettask/webui/namespace_manager/__init__.py +0 -10
- jettask/webui/namespace_manager/multi.py +0 -593
- jettask/webui/namespace_manager/unified.py +0 -193
- jettask/webui/run.py +0 -46
- jettask-0.2.23.dist-info/RECORD +0 -145
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/WHEEL +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
"""
|
2
|
+
时间处理工具函数
|
3
|
+
"""
|
4
|
+
import json
|
5
|
+
from datetime import datetime, timezone
|
6
|
+
from typing import Dict, Any, List
|
7
|
+
|
8
|
+
|
9
|
+
def parse_iso_datetime(time_str: str) -> datetime:
|
10
|
+
"""
|
11
|
+
解析 ISO 格式的时间字符串,确保返回 UTC 时间
|
12
|
+
|
13
|
+
Args:
|
14
|
+
time_str: ISO 格式的时间字符串
|
15
|
+
|
16
|
+
Returns:
|
17
|
+
UTC 时间的 datetime 对象
|
18
|
+
"""
|
19
|
+
if time_str.endswith('Z'):
|
20
|
+
# Z 表示 UTC 时间
|
21
|
+
dt = datetime.fromisoformat(time_str.replace('Z', '+00:00'))
|
22
|
+
else:
|
23
|
+
dt = datetime.fromisoformat(time_str)
|
24
|
+
|
25
|
+
# 如果没有时区信息,假定为 UTC
|
26
|
+
if dt.tzinfo is None:
|
27
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
28
|
+
# 如果有时区信息,转换为 UTC
|
29
|
+
elif dt.tzinfo != timezone.utc:
|
30
|
+
dt = dt.astimezone(timezone.utc)
|
31
|
+
|
32
|
+
return dt
|
33
|
+
|
34
|
+
|
35
|
+
def format_task_timestamps(task: Dict[str, Any], fields: List[str] = None) -> Dict[str, Any]:
|
36
|
+
"""
|
37
|
+
将任务对象中的时间字段转换为 ISO 格式字符串
|
38
|
+
|
39
|
+
Args:
|
40
|
+
task: 任务字典
|
41
|
+
fields: 需要转换的字段列表,默认为 ['created_at', 'started_at', 'completed_at']
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
转换后的任务字典
|
45
|
+
"""
|
46
|
+
if fields is None:
|
47
|
+
fields = ['created_at', 'started_at', 'completed_at']
|
48
|
+
|
49
|
+
for field in fields:
|
50
|
+
if task.get(field):
|
51
|
+
# PostgreSQL 的 TIMESTAMP WITH TIME ZONE 会返回 aware datetime
|
52
|
+
if task[field].tzinfo is None:
|
53
|
+
# 如果没有时区信息,假定为 UTC
|
54
|
+
task[field] = task[field].replace(tzinfo=timezone.utc)
|
55
|
+
task[field] = task[field].isoformat()
|
56
|
+
|
57
|
+
return task
|
58
|
+
|
59
|
+
|
60
|
+
def parse_task_json_fields(task: Dict[str, Any], fields: List[str] = None) -> Dict[str, Any]:
|
61
|
+
"""
|
62
|
+
解析任务对象中的 JSON 字段
|
63
|
+
|
64
|
+
Args:
|
65
|
+
task: 任务字典
|
66
|
+
fields: 需要解析的字段列表,默认为 ['task_data', 'result', 'metadata']
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
解析后的任务字典
|
70
|
+
"""
|
71
|
+
if fields is None:
|
72
|
+
fields = ['task_data', 'result', 'metadata']
|
73
|
+
|
74
|
+
for field in fields:
|
75
|
+
if task.get(field) and isinstance(task[field], str):
|
76
|
+
try:
|
77
|
+
task[field] = json.loads(task[field])
|
78
|
+
except:
|
79
|
+
pass
|
80
|
+
|
81
|
+
return task
|
82
|
+
|
83
|
+
|
84
|
+
def task_obj_to_dict(task_obj) -> Dict[str, Any]:
|
85
|
+
"""
|
86
|
+
将 Task ORM 对象转换为字典
|
87
|
+
|
88
|
+
Args:
|
89
|
+
task_obj: Task ORM 对象
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
任务字典
|
93
|
+
"""
|
94
|
+
task = {
|
95
|
+
'id': task_obj.id,
|
96
|
+
'queue_name': task_obj.queue_name,
|
97
|
+
'task_name': task_obj.task_name,
|
98
|
+
'task_data': task_obj.task_data,
|
99
|
+
'priority': task_obj.priority,
|
100
|
+
'retry_count': task_obj.retry_count,
|
101
|
+
'max_retry': task_obj.max_retry,
|
102
|
+
'status': task_obj.status,
|
103
|
+
'result': task_obj.result,
|
104
|
+
'error_message': task_obj.error_message,
|
105
|
+
'created_at': task_obj.created_at,
|
106
|
+
'started_at': task_obj.started_at,
|
107
|
+
'completed_at': task_obj.completed_at,
|
108
|
+
'worker_id': task_obj.worker_id,
|
109
|
+
'execution_time': task_obj.execution_time,
|
110
|
+
'duration': task_obj.duration,
|
111
|
+
'metadata': task_obj.task_metadata,
|
112
|
+
'next_sync_time': task_obj.next_sync_time,
|
113
|
+
'sync_check_count': task_obj.sync_check_count
|
114
|
+
}
|
115
|
+
|
116
|
+
# 转换时间戳为 ISO 格式
|
117
|
+
task = format_task_timestamps(task)
|
118
|
+
|
119
|
+
# 解析 JSON 字段
|
120
|
+
task = parse_task_json_fields(task)
|
121
|
+
|
122
|
+
return task
|
jettask/worker/lifecycle.py
CHANGED
@@ -366,6 +366,12 @@ class WorkerStateManager:
|
|
366
366
|
pubsub = self.redis.pubsub()
|
367
367
|
await pubsub.subscribe(self.worker_state_channel)
|
368
368
|
|
369
|
+
# 标记PubSub连接,防止被空闲连接清理
|
370
|
+
if hasattr(pubsub, 'connection') and pubsub.connection:
|
371
|
+
pubsub.connection._is_pubsub_connection = True
|
372
|
+
socket_timeout = pubsub.connection.socket_timeout if hasattr(pubsub.connection, 'socket_timeout') else 'N/A'
|
373
|
+
logger.info(f"Marked PubSub connection {id(pubsub.connection)} to prevent cleanup, socket_timeout={socket_timeout}")
|
374
|
+
|
369
375
|
logger.debug(f"Created and subscribed to Redis Pub/Sub channel: {self.worker_state_channel}")
|
370
376
|
return pubsub
|
371
377
|
|
@@ -739,7 +745,7 @@ class HeartbeatThreadManager:
|
|
739
745
|
heartbeat_timeout=15.0):
|
740
746
|
"""初始化心跳线程管理器"""
|
741
747
|
if redis_url is not None:
|
742
|
-
from jettask.
|
748
|
+
from jettask.db.connector import get_sync_redis_client
|
743
749
|
self.redis_client = get_sync_redis_client(redis_url, decode_responses=True)
|
744
750
|
self.redis_url = redis_url
|
745
751
|
self.consumer_id = consumer_id
|
@@ -992,7 +998,7 @@ class HeartbeatThreadManager:
|
|
992
998
|
pass
|
993
999
|
try:
|
994
1000
|
if self.redis_url:
|
995
|
-
from jettask.
|
1001
|
+
from jettask.db.connector import get_sync_redis_client
|
996
1002
|
self.redis_client = get_sync_redis_client(
|
997
1003
|
redis_url=self.redis_url,
|
998
1004
|
decode_responses=True,
|
@@ -0,0 +1,142 @@
|
|
1
|
+
jettask/__init__.py,sha256=4TsONE4CfUIRgLvZMDvCOoEZWCA2qx11ZONaaqmhsls,2012
|
2
|
+
jettask/cli.py,sha256=0pxM2Kvoa-hM2gE3q_nYXjfWJy_8pXXZSxIjKe3z8uw,38708
|
3
|
+
jettask/exceptions.py,sha256=aQTUpiF2ftiZwGORUHXatAKbDEzptUf6iP7ZOXBQ3tQ,1971
|
4
|
+
jettask/test_connection_monitor.py,sha256=aJmWDcC8XoJVEKfnQbixof9VysxFUMfHvIoI2TV1eS0,2189
|
5
|
+
jettask/config/__init__.py,sha256=gCfoHVfTWIRPbtY1-iE4ydf2lfOmEwTU3EoSJ7NbLIU,273
|
6
|
+
jettask/config/config.py,sha256=YdResg9uUD1rbmoumoIE-FkhMZ_skoRLgTlUjkLEEzQ,7111
|
7
|
+
jettask/config/constants.py,sha256=rw06U4WPSFk6f80blC0ebkqnPUS_eteKQesTSBXKn6A,7028
|
8
|
+
jettask/config/env_loader.py,sha256=P2uO63mAHfhu9Pqa_Hh0JKWmeFDptudSHgBI-fyr0sA,10558
|
9
|
+
jettask/config/lua_scripts.py,sha256=40nFzJNm6cZHfKZ7ovYRMVofS5bxWFGU-Dq0RS4PCdo,6070
|
10
|
+
jettask/config/nacos_config.py,sha256=PPmb-2gYTfgrdianjQdB9B4xYLAfRSihHEalccZXpo8,19689
|
11
|
+
jettask/config/performance.py,sha256=bOdLEskfB_6cRfS10IRgmtKEsJw_CaIZsPHbXxaHwbU,5686
|
12
|
+
jettask/config/task_center.py,sha256=dJ9UkWRR1kulHFhfSVXmT_bM-N7yEMJdp0JylUkrLZ4,2062
|
13
|
+
jettask/core/__init__.py,sha256=3qnUjllxsvy9gqJEKnJbQeJcSEmxdWUwwzumdJG5vKI,198
|
14
|
+
jettask/core/app.py,sha256=ycR5G9JBSMi941MyCnC0RG_3RyI4wpl1XQgq20F16NQ,103722
|
15
|
+
jettask/core/app_importer.py,sha256=04wRDCp5NFNiDkDhMOM7CdaHS_0b0cYylnYpXie8fwM,11018
|
16
|
+
jettask/core/container.py,sha256=Q4eGqkaw6C8jW3PyrmG5RrcCz3NA1ky2HelPx4Nn-xU,18108
|
17
|
+
jettask/core/context.py,sha256=XI4Q1s75NIWImcrHIkCLgGo_kpuJIk8MlBoNIJuEfF0,993
|
18
|
+
jettask/core/enums.py,sha256=ufXI5OcGZoOSbLEyAWtnkZVO-LcqNbIOygyjLLa6ce8,4632
|
19
|
+
jettask/core/message.py,sha256=WDOY8kvoV6at4MsIP01c3GJmIwHvRnUHimli6NKcw8w,4340
|
20
|
+
jettask/core/namespace.py,sha256=_EVMGWB4BrHf5Z6Zmdof3Uim9uX7-4IQ40HjLmE_WRM,28616
|
21
|
+
jettask/core/retry.py,sha256=lX56Z0fvKjEhVbYPHYdoN-W4XUPVplHlfIOupBylEeI,10711
|
22
|
+
jettask/core/task.py,sha256=Ada-wJLZLexrZtKLVqRDFSQOsvAnQpZnxPTDdoJnQq0,5712
|
23
|
+
jettask/core/unified_manager_base.py,sha256=JBBZUEolQHlHbeuNU3k8TwOBh7kxgs5z4yb8FbhbdKg,18987
|
24
|
+
jettask/db/__init__.py,sha256=FOYiPJs8B2ek-SgaszCTcBuzWaMKKTAwPTfqBHBDIJo,1388
|
25
|
+
jettask/db/base.py,sha256=vhrPErPKIkSsAEcF7l3FCZ4e4iNUjocwH0mkQ7MECQI,3769
|
26
|
+
jettask/db/connector.py,sha256=f3yfOhjadXDQEU9hBHIciV80tze3ugkaFn1uQgQz7To,63906
|
27
|
+
jettask/db/models/__init__.py,sha256=M9WlVfOOvovX5b_4aO0_NsEiQrxcV8zih2nITSbWiis,277
|
28
|
+
jettask/db/models/scheduled_task.py,sha256=Xxz3ayhyAdoFhCPEJUmDpNgp6pZdhhzCByp6PI9DiRc,7263
|
29
|
+
jettask/db/models/task.py,sha256=oliM4ZHM6bIANvlYGLjhivgmcGAvG_686--aRxCHljE,2441
|
30
|
+
jettask/db/models/task_run.py,sha256=SYIJtrAnnQMO6VvrK0_eD4z24gVHgRS6uoUwQwQDhLs,2826
|
31
|
+
jettask/executor/__init__.py,sha256=mdGkv9IsTxSJEfMg8b8mzw_RIGuPT05o2OxHxpKUWWQ,498
|
32
|
+
jettask/executor/core.py,sha256=Nf7tZ0ZxuSfKDqYzwQTIlFtdsNjWTbxj6-UHVVdTAVs,27119
|
33
|
+
jettask/executor/orchestrator.py,sha256=DS5dYnYnp7sISLb5hLBcPNhP-Tqvd5NxhoBE-4iWxRQ,9986
|
34
|
+
jettask/executor/process_entry.py,sha256=wr_0qXD5O_wZfkXRHRaW7ZJXZ97iWtPcEjt9c4njdnI,19510
|
35
|
+
jettask/executor/task_executor.py,sha256=RqHE6K0bcvZ72-Wkc5oGl41KIuFvZ--6Y8bqi35vtXg,11270
|
36
|
+
jettask/messaging/__init__.py,sha256=hWv1p5kmud8nTbx-KceKkmaYbvYWhzJWNR8OzTvoRP0,1961
|
37
|
+
jettask/messaging/event_pool.py,sha256=m2xacXiaflsTCRCaWWbH07_RHDezNgDYONY2EK_lm2I,118256
|
38
|
+
jettask/messaging/reader.py,sha256=hlOYzU1LJrm7OhKb5iBrUjqHjsN3sN5FGKYHLiHGHes,17556
|
39
|
+
jettask/messaging/registry.py,sha256=wG6SvyoCFMDggGd_SaDlLKaBcUPWzlblcGLMRhJwx90,10290
|
40
|
+
jettask/messaging/scanner.py,sha256=jRr2-RXfMNTLfU6v9ZX4QEPPBZdTqjwq9JSvMz8WkRQ,15496
|
41
|
+
jettask/messaging/sender.py,sha256=cz98GiSMQhKxUyEkPmOHUIG78GFMW_f-P4j-KUCVMrE,10051
|
42
|
+
jettask/messaging/pg_consumer/pg_consumer_v2.py,sha256=ajd6ZM_HY9YWK_I5BNbGuWxNqSS892d3ZzvDlTSQgwc,16211
|
43
|
+
jettask/messaging/pg_consumer/sql_utils.py,sha256=dZM_8kIzUmRq0me4yLwb3UeoDvIVKJdIc9VNP1nbS0E,6461
|
44
|
+
jettask/messaging/pg_consumer/sql/add_execution_time_field.sql,sha256=z2Jx-0uG-IW5RsTGxcLFu2_BcMReluEptMJWB6Qrj08,1184
|
45
|
+
jettask/messaging/pg_consumer/sql/create_new_tables.sql,sha256=layb08_MFGVSE-NQEcjlQBklZ307JtJaccrnqkcj7Vg,6566
|
46
|
+
jettask/messaging/pg_consumer/sql/create_tables_v3.sql,sha256=ZwBjxKWU6imWACoWMCIACLsYUPiPep9WmJsdIjI2a58,8173
|
47
|
+
jettask/messaging/pg_consumer/sql/migrate_to_new_structure.sql,sha256=elhObWvbtqAhb4osJC6D5JhxcOxhGJgIBqx4ItB_xO8,5716
|
48
|
+
jettask/messaging/pg_consumer/sql/modify_time_fields.sql,sha256=mRfoXbP9iXbAJBj-xNume8-4AV_UPQcBALNUqEf3aKw,2572
|
49
|
+
jettask/persistence/__init__.py,sha256=9vU0k8ANmQxMBsd0Vtj-gd_ZNQoS8jJ2Bl8Cj2AsWTE,1228
|
50
|
+
jettask/persistence/buffer.py,sha256=Pd0EBT23c2mSdpSG5j4PGguj1mdIpBjyx6kkAVHooz8,5347
|
51
|
+
jettask/persistence/consumer.py,sha256=Krt_xY2he88Ge08uYq_idEnFb-yD_523Z1lygOYX8Ek,15589
|
52
|
+
jettask/persistence/manager.py,sha256=gPjdVu135lDniBuceHyaRyX2HhFC9uNvbyUhiNeycYY,11660
|
53
|
+
jettask/persistence/persistence.py,sha256=S_GEIRAdLVM60uYF93WUBWe5H5LJ01ZvnuALNY8Mxkw,16494
|
54
|
+
jettask/scheduler/__init__.py,sha256=vlNbwGS0r3ednD73ZD9nM4kFdbPk8WMQHIsM3qfMP5A,856
|
55
|
+
jettask/scheduler/database.py,sha256=HA3mAD9IN-zpjD7Ps70V0-gTG5g3WHq225LW0fRvAEk,26736
|
56
|
+
jettask/scheduler/loader.py,sha256=KtDdKnn2cF1Qegv5fU-BAfeCRlJR89VcB75JzjawBM8,8808
|
57
|
+
jettask/scheduler/manager.py,sha256=MQmSN7okmxrBq-i10fj_H1yarcxWgDDLfVauEbMbSrU,13003
|
58
|
+
jettask/scheduler/models.py,sha256=sSp_ItOb6d4ZW0wGN9LxrR475I0dJSuF2u9gHjrZEWs,7497
|
59
|
+
jettask/scheduler/schedule.py,sha256=OE0z4NCa85LOQYT6eRm37rlhsgSPYh-rXG6Qfgq4aEE,5718
|
60
|
+
jettask/scheduler/scheduler.py,sha256=Zj0nqEvDp-QfS5xl03oKzkJWU3G4PHxIVfcV36BYSZw,27842
|
61
|
+
jettask/schemas/__init__.py,sha256=COaNqPbLi71ColUk8PsZVV8VSzdAiS7Ad1IkBh5IL9s,4329
|
62
|
+
jettask/schemas/alert.py,sha256=Od29QjDrnLN1Tw_F2rPFSGsqRvbBcO2hf1d8GO1_WEQ,4958
|
63
|
+
jettask/schemas/backlog.py,sha256=SJVzNG7Vwm1tOWW4aAZMGLwsOxfv1FlmCK6HfOoWTws,6600
|
64
|
+
jettask/schemas/common.py,sha256=e1lrLnOpxKT25FH7QnDQy38Qj4SGqd_cwHvRObTuRZw,6038
|
65
|
+
jettask/schemas/monitoring.py,sha256=zPfNv6YXw3uufj61PseQTaQkpwa9azgsnCGUaF4HNUk,8084
|
66
|
+
jettask/schemas/namespace.py,sha256=9kYRBXGBqGV6i8EBmB4PTAmTG0ex2xOffZaUgMIDxvw,10870
|
67
|
+
jettask/schemas/queue.py,sha256=qy8Ty8BseRPyXmfDOpFgojTvYhsaBAJnpzFK2ZSYF_s,4516
|
68
|
+
jettask/schemas/responses.py,sha256=StGoD6Y2pNtU6V9t1xpbHoSWjigk_YBddai09SYDwqM,16522
|
69
|
+
jettask/schemas/scheduled_task.py,sha256=MPAXvDeqmyQTWhL31yS94mRuzJWOV4njukp6BQrX6K4,7130
|
70
|
+
jettask/schemas/schema.sql,sha256=IY-vLzj3TYoR4p51ChLuSxzo4KU1PQNFNDutc7p0nHg,4490
|
71
|
+
jettask/schemas/task.py,sha256=niW2F_84HvstAjX3vo2pZswoRwMCF3DIoONuHwWNwtk,3172
|
72
|
+
jettask/task/__init__.py,sha256=IR3c7HJ3NK25YzwrlKBhpyUcbv5kGnDQ9ogzcmR1zdU,246
|
73
|
+
jettask/task/router.py,sha256=nyHR4SAAFsWbeQh3Hza5U5a_w7l0WyZgsbUki9TzzhM,5900
|
74
|
+
jettask/task/task_registry.py,sha256=HYq6S9XWvavhy7UJeOGQSTR5LF0V_vyACcZ12yIMS68,7987
|
75
|
+
jettask/task/task_center/__init__.py,sha256=Ed_D427tD59wTeqfbjckB-qKrCPnRLuzWovpQRW9uFs,132
|
76
|
+
jettask/task/task_center/client.py,sha256=BcJOaKAeUKDl75GQzoQdBkZdBiC4gPxLrNa5VbKC86I,8537
|
77
|
+
jettask/utils/__init__.py,sha256=qyE5dRmgC5qYWtjGUHpIid9LOG65oHcv-Nx1uptfIRI,960
|
78
|
+
jettask/utils/backlog_collector.py,sha256=U3_TGHtcD4CbKOyFkbOShht8wkEeCwJ38QiZTvDgEn0,2523
|
79
|
+
jettask/utils/db_init.py,sha256=9E7hk8V-AYgffJ3WNbt34_7TKR1enOF3kBgFTcv1o2E,15486
|
80
|
+
jettask/utils/error_handler.py,sha256=9iYHCInUkEkP9o07dUgPxQr4aWqR5FUhnNWLgB0TEqM,2794
|
81
|
+
jettask/utils/exception_hook.py,sha256=Fp0m71_jjmmZvyoELARSsPIvSVPCmMQMVC1FxMFOoHQ,1765
|
82
|
+
jettask/utils/file_watcher.py,sha256=r3Mgekb_5sOssDrnFBCbbyvpWkwD2ZPA_j22ztzRCT0,1207
|
83
|
+
jettask/utils/helpers.py,sha256=uTdyRFZsdwSQzuEcNoGgADuVnHaDEYq6JQP6zVxAj04,3368
|
84
|
+
jettask/utils/logger.py,sha256=PcZgUtx7I9pe90Y8WgPQtKhMefd1oQ14pgUlKCFpCXo,843
|
85
|
+
jettask/utils/namespace_dep.py,sha256=JVcBqRPYwhGtmWFg-OKoPkxkeYCU4IxsGjx6PRF_jqM,5168
|
86
|
+
jettask/utils/queue_matcher.py,sha256=AP7Zh1WDMh2vG8XB7JCMAaepAb37rGi0O2HuMgBJxng,5430
|
87
|
+
jettask/utils/redis_monitor.py,sha256=2l6fF2qAtctS9HUaEV3acJMJ5zhkvT4iygJGhGBCoKw,3660
|
88
|
+
jettask/utils/serializer.py,sha256=Omnnbpp9FuRDugqan2QK75kVV91Rv07kN-hzqCC_3G4,856
|
89
|
+
jettask/utils/stream_backlog.py,sha256=4F-AiRngdby9CW8evdwWGLYrMEeTbNrCFMrbfB7RHcg,15971
|
90
|
+
jettask/utils/task_logger.py,sha256=kauCgiDDzhuge3wk-BXR_KwytOfmrrVBKe1S4XUWBXU,13628
|
91
|
+
jettask/utils/time_sync.py,sha256=DcZWboLKa9DwyQgDGGZGNLrQrMRVaJXJLk0xCDUW0B4,5209
|
92
|
+
jettask/utils/traceback_filter.py,sha256=2svLo5_OoosAbS08UpceeWtjSKkdx-Fp6U-ysJQkroY,3025
|
93
|
+
jettask/utils/rate_limit/__init__.py,sha256=Ahl96s0BqSofOIsQUIHfc2QpO46GezjtLXC7QzOFpKs,810
|
94
|
+
jettask/utils/rate_limit/concurrency_limiter.py,sha256=NPHlPwhudoceobgIb2BsuKrLxUaOsZIF0G9lcZoKVDA,27718
|
95
|
+
jettask/utils/rate_limit/config.py,sha256=ChOQ2AWUBMbq3lTImYZ59UBwDPAetJ3UcV0WuhHPUgY,3862
|
96
|
+
jettask/utils/rate_limit/limiter.py,sha256=itH_APz7m5qBntJtlfF120OvkD1N7hKuiwPtu6OF28U,1285
|
97
|
+
jettask/utils/rate_limit/manager.py,sha256=R9dqczrJYpVhcecMASEYdnPDKmSVaNwmdnzrzLvadZg,9535
|
98
|
+
jettask/utils/rate_limit/qps_limiter.py,sha256=npo6_Sw51AT9I4OvRfsYZddViF6cZQl5DpW8XDWRRBo,4787
|
99
|
+
jettask/utils/rate_limit/task_limiter.py,sha256=7QaK1IR8rF-h1e-4-loxFL5tBiQPceFXLCdqcK8Rpl4,14072
|
100
|
+
jettask/webui/__init__.py,sha256=OcPwYBgPX1PSWF_Hdnxaq_2N2jX-VCdyrs-MYrwkmds,570
|
101
|
+
jettask/webui/app.py,sha256=vcQ7-gYnN8mf8z9BCMqzNuq11S9crd2eAQ12AU8CiNo,4657
|
102
|
+
jettask/webui/api/__init__.py,sha256=GYl42r7ZtGtqOtiFUeyrKsNDYKTo8jS03Pci-sQxqm8,1200
|
103
|
+
jettask/webui/api/alerts.py,sha256=Vy9PwihQyaFtoFXeuyxNZcVQxJpIR1HIAkVYzpj37hY,29540
|
104
|
+
jettask/webui/api/example_refactored.py,sha256=aVaGDuIrrdlfjmrXnfusWWCRz4SdDGIj4CxCBDcUY5Y,13603
|
105
|
+
jettask/webui/api/namespaces.py,sha256=hY3tSplUjteQdITjKdgrd0X06T8k8-EAPW29lQSW-zQ,14637
|
106
|
+
jettask/webui/api/overview.py,sha256=tyf9xlW7r-ztnZDmgacih_QUNhR7DFhwpTrpasT8ZdQ,12014
|
107
|
+
jettask/webui/api/queues.py,sha256=SU1mjf3VL6qL3biWAQVkX9NhHwcgoSZ-GJuPQH_LQeQ,42341
|
108
|
+
jettask/webui/api/scheduled.py,sha256=XKqgnRwrRrMZ1zGtdO0x6aKMBOwBEy1-2BXdDzvSE6o,54370
|
109
|
+
jettask/webui/api/settings.py,sha256=vPg1p8TkNK0E9tml6nZY8yxdUo4zJ0pL2s0eqBjuPaA,4435
|
110
|
+
jettask/webui/api/workers.py,sha256=pK7G5bvFic5Nhgot1HqhJBtcygUkMS34RnENldHyUHg,14101
|
111
|
+
jettask/webui/middleware/__init__.py,sha256=v_5RM0XlGEYL4VGiPG8Jy12bIkOtHzhkkXXzh_uGJh0,119
|
112
|
+
jettask/webui/middleware/namespace_middleware.py,sha256=kZ4k1rnlOibuYzhSejcBc_G5X5nmh6vK3rndJZXHnd4,4926
|
113
|
+
jettask/webui/services/__init__.py,sha256=PyUMoa6tEEYL7VFJkblIB7r_4ojr4vNls3qUfvx3ooU,6109
|
114
|
+
jettask/webui/services/alert_service.py,sha256=AIk4hjx-yoFnl5vPff7TMwjgjheKu91da9mZ5gYDroo,14169
|
115
|
+
jettask/webui/services/analytics_service.py,sha256=zLTtGUJ7wYIU7O5NP_VS_-wk_IXNfxKikAvQ3aaeCmI,1101
|
116
|
+
jettask/webui/services/heartbeat_service.py,sha256=Srp3IoVJaoz2EF-dobH9Eq0bSVJGz8s9LEZlT1ByxyY,9386
|
117
|
+
jettask/webui/services/overview_service.py,sha256=kuNz-AxRFa_z6bAytA05J797MNBHYlp2qvQbMjBOgEA,37102
|
118
|
+
jettask/webui/services/queue_monitor_service.py,sha256=XkJuwFrVO3GqKcfC9Ll3d-XV2Nxt1bISAfJJF_RBuZw,14927
|
119
|
+
jettask/webui/services/queue_service.py,sha256=3ONDfGos2xHtHZlWcAlaXzEo1Km-HTIllCpN3GPKKG0,27419
|
120
|
+
jettask/webui/services/queue_stats_v2.py,sha256=DW46_w1-3k9PozZScLUen0-wbHMTgxo346DLVmxsw5Q,24409
|
121
|
+
jettask/webui/services/redis_monitor_service.py,sha256=Ot1POh4GS3qbFMx03kjDDox53tnINxTepcUQmt8KfTU,2524
|
122
|
+
jettask/webui/services/scheduled_task_service.py,sha256=ZFFtcUdk_IDGm17maHA1Hb0p00_56QqQ9ETFAF7T1bU,6226
|
123
|
+
jettask/webui/services/settings_service.py,sha256=nyeTPF0DaLtNfk6h2uHfzIzf7L53GV9zhfL4bstxE_Q,35592
|
124
|
+
jettask/webui/services/task_monitor_service.py,sha256=lLWGi9eO1gmemV0Wxa2h5S1uP0J5gXAGYYUb6ZMiJUU,7804
|
125
|
+
jettask/webui/services/task_service.py,sha256=vfSEyhFxDcEzOuA4sHEg1sYvLriUUvJ08SQ2ICG7MSU,4751
|
126
|
+
jettask/webui/services/timeline_pg_service.py,sha256=6IScSTnK_ZCzg7_EGs0CLrac1RTqLzhp7KsrnPjsOFM,17467
|
127
|
+
jettask/webui/services/timeline_service.py,sha256=79QqB7nAQGj3vHl1Ubf1ao7ALRFahiaPr2px4wHCnOI,6434
|
128
|
+
jettask/webui/services/worker_monitor_service.py,sha256=KTjJsMLHrHKIRYWi9RHKNjIXIHzMNAC-nqdXh0XdSnM,17323
|
129
|
+
jettask/webui/sql/batch_upsert_functions.sql,sha256=5eWOhOD8gWHhtcop_BrCpZTxPFeyBHtt_leNQZO89Cs,6615
|
130
|
+
jettask/webui/sql/verify_database.sql,sha256=HtTup3xHWbOo1BTU_u4i41E9LrPEXB8qYbChL9WeWOc,2313
|
131
|
+
jettask/webui/utils/__init__.py,sha256=4Ne5wAOwejOzKZVk6DRQ6apOI9mTS_tu9TOvKAFL5gk,208
|
132
|
+
jettask/webui/utils/time_utils.py,sha256=UwBkt0ZKH5wpDlRZx6wvTlacSlqAbMQ9cIhlDZnArgc,3426
|
133
|
+
jettask/worker/__init__.py,sha256=HUwUjZEWV6oaGJSUg26PPwVsIacAB8Tk8qzQQYvlMLM,1218
|
134
|
+
jettask/worker/lifecycle.py,sha256=ioSIw2TidBnGpHrryIzQE4cgSDjiUAlClPBUtrAlf1s,58989
|
135
|
+
jettask/worker/manager.py,sha256=fIayGpH9nLqvxTv-hDwrVj41sQqid96ZzNjENCLR1Nk,19913
|
136
|
+
jettask/worker/recovery.py,sha256=I7Au9DPpsEhlILxYcnnC0H-Qw51rr97sUsy2pn43Czw,29120
|
137
|
+
jettask-0.2.24.dist-info/licenses/LICENSE,sha256=sKR8OPWvnqxzcHAmnaVSANpRpeM0Z52PNLCB0ZlFN6c,1062
|
138
|
+
jettask-0.2.24.dist-info/METADATA,sha256=Yd9LfUdxKRpsHlYmSx2vvqG0gd0GxhHm8pqbqVWbAWY,1949
|
139
|
+
jettask-0.2.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
140
|
+
jettask-0.2.24.dist-info/entry_points.txt,sha256=9-8eTLJPgzpBgeGC8WHdMYtldZxHAXFspMeqArVUdew,148
|
141
|
+
jettask-0.2.24.dist-info/top_level.txt,sha256=uymyRUF87-OsSurk5NhpeTW0jy3Wltnn91Zoa6jmAaw,8
|
142
|
+
jettask-0.2.24.dist-info/RECORD,,
|
jettask/executor/executor.py
DELETED
@@ -1,338 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
统一执行器
|
3
|
-
|
4
|
-
整合了单进程和多进程执行模式的统一接口
|
5
|
-
"""
|
6
|
-
|
7
|
-
import asyncio
|
8
|
-
import multiprocessing
|
9
|
-
import logging
|
10
|
-
import os
|
11
|
-
import time
|
12
|
-
from collections import deque
|
13
|
-
from typing import List, Optional
|
14
|
-
|
15
|
-
from .core import ExecutionMode, ExecutorCore
|
16
|
-
from .orchestrator import ProcessConfig, ProcessOrchestrator
|
17
|
-
from ..worker.lifecycle import WorkerStateManager
|
18
|
-
from ..utils.rate_limit.manager import RateLimiterManager
|
19
|
-
|
20
|
-
logger = logging.getLogger('app')
|
21
|
-
|
22
|
-
# Try to use uvloop for better performance
|
23
|
-
try:
|
24
|
-
import uvloop
|
25
|
-
uvloop.install()
|
26
|
-
logger.debug("Using uvloop for better performance")
|
27
|
-
except ImportError:
|
28
|
-
pass
|
29
|
-
|
30
|
-
|
31
|
-
class UnifiedExecutor:
|
32
|
-
"""
|
33
|
-
统一执行器
|
34
|
-
|
35
|
-
整合AsyncioExecutor和MultiAsyncioExecutor的功能
|
36
|
-
支持单进程和多进程两种执行模式
|
37
|
-
|
38
|
-
职责:
|
39
|
-
1. 提供统一的执行器接口
|
40
|
-
2. 根据模式选择ExecutorCore或ProcessOrchestrator
|
41
|
-
3. 管理事件队列和任务分发
|
42
|
-
"""
|
43
|
-
|
44
|
-
def __init__(self, event_queue, app, concurrency=100,
|
45
|
-
mode: ExecutionMode = ExecutionMode.SINGLE_PROCESS,
|
46
|
-
task_name: str = None):
|
47
|
-
"""
|
48
|
-
初始化统一执行器
|
49
|
-
|
50
|
-
Args:
|
51
|
-
event_queue: 事件队列
|
52
|
-
app: Application实例
|
53
|
-
concurrency: 并发数
|
54
|
-
mode: 执行模式
|
55
|
-
task_name: 任务名称(单进程模式必需)
|
56
|
-
"""
|
57
|
-
self.event_queue = event_queue
|
58
|
-
self.app = app
|
59
|
-
self.concurrency = concurrency
|
60
|
-
self.mode = mode
|
61
|
-
self.task_name = task_name
|
62
|
-
|
63
|
-
# 根据模式初始化核心组件
|
64
|
-
if mode == ExecutionMode.SINGLE_PROCESS:
|
65
|
-
if not task_name:
|
66
|
-
raise ValueError("task_name is required for SINGLE_PROCESS mode")
|
67
|
-
|
68
|
-
self.executor_core = ExecutorCore(
|
69
|
-
app=app,
|
70
|
-
task_name=task_name,
|
71
|
-
concurrency=concurrency
|
72
|
-
)
|
73
|
-
self.orchestrator = None
|
74
|
-
logger.debug(f"UnifiedExecutor initialized in SINGLE_PROCESS mode for task {task_name}")
|
75
|
-
|
76
|
-
elif mode == ExecutionMode.MULTI_PROCESS:
|
77
|
-
self.executor_core = None
|
78
|
-
self.orchestrator = ProcessOrchestrator(
|
79
|
-
app=app,
|
80
|
-
num_processes=concurrency
|
81
|
-
)
|
82
|
-
logger.debug(f"UnifiedExecutor initialized in MULTI_PROCESS mode with {concurrency} processes")
|
83
|
-
|
84
|
-
else:
|
85
|
-
raise ValueError(f"Unsupported execution mode: {mode}")
|
86
|
-
|
87
|
-
# 活动任务集合(单进程模式使用)
|
88
|
-
self._active_tasks = set()
|
89
|
-
|
90
|
-
def logic(self, *args, **kwargs):
|
91
|
-
"""
|
92
|
-
BaseExecutor接口方法
|
93
|
-
在单进程模式下不使用,多进程模式委托给ProcessOrchestrator
|
94
|
-
"""
|
95
|
-
pass
|
96
|
-
|
97
|
-
async def loop(self):
|
98
|
-
"""主循环 - 单进程模式"""
|
99
|
-
if self.mode != ExecutionMode.SINGLE_PROCESS:
|
100
|
-
raise RuntimeError("loop() is only for SINGLE_PROCESS mode")
|
101
|
-
|
102
|
-
# 初始化限流器
|
103
|
-
self.app.consumer_manager._heartbeat_strategy._ensure_consumer_id()
|
104
|
-
worker_id = self.app.consumer_manager._heartbeat_strategy.consumer_id
|
105
|
-
registry_manager = self.app.consumer_manager
|
106
|
-
|
107
|
-
if not self.app.worker_state_manager:
|
108
|
-
self.app.worker_state_manager = WorkerStateManager(
|
109
|
-
redis_client=self.app.ep.async_redis_client,
|
110
|
-
redis_prefix=self.executor_core.prefix,
|
111
|
-
event_pool=self.app.ep # 传入 EventPool 实例,启用事件驱动的消息恢复
|
112
|
-
)
|
113
|
-
await self.app.worker_state_manager.start_listener()
|
114
|
-
logger.debug(f"WorkerStateManager started for worker {worker_id}")
|
115
|
-
|
116
|
-
# # 初始化时间同步
|
117
|
-
from jettask.utils.time_sync import init_time_sync
|
118
|
-
time_sync = await init_time_sync(self.app.ep.async_redis_client)
|
119
|
-
logger.debug(f"TimeSync initialized, offset={time_sync.get_offset():.6f}s")
|
120
|
-
|
121
|
-
self.executor_core.rate_limiter_manager = RateLimiterManager(
|
122
|
-
redis_client=self.app.ep.async_redis_client,
|
123
|
-
worker_id=worker_id,
|
124
|
-
redis_prefix=self.executor_core.prefix,
|
125
|
-
registry_manager=registry_manager,
|
126
|
-
worker_state_manager=self.app.worker_state_manager
|
127
|
-
)
|
128
|
-
logger.debug(f"RateLimiterManager initialized for worker {worker_id}")
|
129
|
-
|
130
|
-
await self.executor_core.rate_limiter_manager.load_config_from_redis()
|
131
|
-
|
132
|
-
tasks_batch = []
|
133
|
-
max_buffer_size = 5000
|
134
|
-
|
135
|
-
try:
|
136
|
-
while True:
|
137
|
-
# 检查退出信号
|
138
|
-
if hasattr(self.app, '_should_exit') and self.app._should_exit:
|
139
|
-
logger.debug("UnifiedExecutor detected shutdown signal")
|
140
|
-
break
|
141
|
-
|
142
|
-
# 检查父进程
|
143
|
-
if hasattr(os, 'getppid') and os.getppid() == 1:
|
144
|
-
logger.debug("Parent process died, exiting...")
|
145
|
-
break
|
146
|
-
|
147
|
-
current_time = time.time()
|
148
|
-
|
149
|
-
# 获取事件
|
150
|
-
event = None
|
151
|
-
try:
|
152
|
-
event = await asyncio.wait_for(self.event_queue.get(), timeout=0.1)
|
153
|
-
except asyncio.TimeoutError:
|
154
|
-
event = None
|
155
|
-
|
156
|
-
if event:
|
157
|
-
event.pop("execute_time", None)
|
158
|
-
tasks_batch.append(event)
|
159
|
-
logger.debug(f"[EVENT] Got event: {event.get('event_id', 'unknown')}, task_name={event.get('event_data', {}).get('_task_name')}")
|
160
|
-
|
161
|
-
# 批量创建任务
|
162
|
-
if tasks_batch:
|
163
|
-
for event in tasks_batch:
|
164
|
-
event_data = event.get('event_data', {})
|
165
|
-
event_task_name = event_data.get("_task_name") or event_data.get("name")
|
166
|
-
|
167
|
-
if not event_task_name:
|
168
|
-
logger.error(f"No task_name in event {event.get('event_id')}")
|
169
|
-
continue
|
170
|
-
|
171
|
-
# 验证任务名称匹配
|
172
|
-
if event_task_name != self.task_name:
|
173
|
-
logger.error(f"Task name mismatch: {event_task_name} != {self.task_name}")
|
174
|
-
continue
|
175
|
-
|
176
|
-
# 限流控制
|
177
|
-
logger.debug(f"[TASK] Attempting to acquire rate limit for {self.task_name}, event_id={event.get('event_id')}")
|
178
|
-
rate_limit_token = await self.executor_core.rate_limiter_manager.acquire(
|
179
|
-
task_name=self.task_name,
|
180
|
-
timeout=None
|
181
|
-
)
|
182
|
-
print(f'{rate_limit_token=}')
|
183
|
-
if not rate_limit_token:
|
184
|
-
logger.error(f"Failed to acquire token for {self.task_name}")
|
185
|
-
continue
|
186
|
-
logger.debug(f"[TASK] Successfully acquired rate limit for {self.task_name}, token={rate_limit_token}, starting execution")
|
187
|
-
|
188
|
-
self.executor_core.batch_counter += 1
|
189
|
-
|
190
|
-
# 创建任务包装器,在任务完成时自动释放限流许可
|
191
|
-
async def execute_with_release(event_data, token):
|
192
|
-
try:
|
193
|
-
await self.executor_core.execute_task(**event_data)
|
194
|
-
finally:
|
195
|
-
# 无论任务成功还是失败,都释放并发许可
|
196
|
-
await self.executor_core.rate_limiter_manager.release(self.task_name, task_id=token)
|
197
|
-
|
198
|
-
task = asyncio.create_task(execute_with_release(event, rate_limit_token))
|
199
|
-
self._active_tasks.add(task)
|
200
|
-
task.add_done_callback(self._active_tasks.discard)
|
201
|
-
|
202
|
-
tasks_batch.clear()
|
203
|
-
|
204
|
-
# 智能缓冲区管理
|
205
|
-
buffer_full = (
|
206
|
-
len(self.executor_core.pending_acks) >= max_buffer_size or
|
207
|
-
len(self.executor_core.status_updates) >= max_buffer_size or
|
208
|
-
len(self.executor_core.data_updates) >= max_buffer_size or
|
209
|
-
len(self.executor_core.task_info_updates) >= max_buffer_size
|
210
|
-
)
|
211
|
-
|
212
|
-
should_flush_periodic = False
|
213
|
-
has_pending_data = (
|
214
|
-
self.executor_core.pending_acks or
|
215
|
-
self.executor_core.status_updates or
|
216
|
-
self.executor_core.data_updates or
|
217
|
-
self.executor_core.task_info_updates
|
218
|
-
)
|
219
|
-
|
220
|
-
if has_pending_data:
|
221
|
-
for data_type, config in self.executor_core.pipeline_config.items():
|
222
|
-
time_since_flush = current_time - self.executor_core.last_pipeline_flush[data_type]
|
223
|
-
|
224
|
-
if data_type == 'ack' and self.executor_core.pending_acks:
|
225
|
-
if time_since_flush >= config['max_delay']:
|
226
|
-
should_flush_periodic = True
|
227
|
-
break
|
228
|
-
elif data_type == 'task_info' and self.executor_core.task_info_updates:
|
229
|
-
if time_since_flush >= config['max_delay']:
|
230
|
-
should_flush_periodic = True
|
231
|
-
break
|
232
|
-
elif data_type == 'status' and self.executor_core.status_updates:
|
233
|
-
if time_since_flush >= config['max_delay']:
|
234
|
-
should_flush_periodic = True
|
235
|
-
break
|
236
|
-
elif data_type == 'data' and self.executor_core.data_updates:
|
237
|
-
if time_since_flush >= config['max_delay']:
|
238
|
-
should_flush_periodic = True
|
239
|
-
break
|
240
|
-
|
241
|
-
if buffer_full or should_flush_periodic:
|
242
|
-
asyncio.create_task(self.executor_core._flush_all_buffers())
|
243
|
-
|
244
|
-
# 智能休眠
|
245
|
-
has_events = False
|
246
|
-
if isinstance(self.event_queue, deque):
|
247
|
-
has_events = bool(self.event_queue)
|
248
|
-
elif isinstance(self.event_queue, asyncio.Queue):
|
249
|
-
has_events = not self.event_queue.empty()
|
250
|
-
|
251
|
-
if has_events:
|
252
|
-
await asyncio.sleep(0)
|
253
|
-
else:
|
254
|
-
if has_pending_data:
|
255
|
-
await self.executor_core._flush_all_buffers()
|
256
|
-
await asyncio.sleep(0.001)
|
257
|
-
|
258
|
-
except KeyboardInterrupt:
|
259
|
-
logger.debug("UnifiedExecutor received KeyboardInterrupt")
|
260
|
-
except Exception as e:
|
261
|
-
logger.error(f"UnifiedExecutor loop error: {e}")
|
262
|
-
finally:
|
263
|
-
await self._cleanup_single_process()
|
264
|
-
|
265
|
-
async def _cleanup_single_process(self):
|
266
|
-
"""清理单进程模式资源"""
|
267
|
-
logger.debug("UnifiedExecutor cleaning up...")
|
268
|
-
|
269
|
-
# 设置停止标志
|
270
|
-
if hasattr(self.app.ep, '_stop_reading'):
|
271
|
-
self.app.ep._stop_reading = True
|
272
|
-
|
273
|
-
# 取消活动任务
|
274
|
-
if self._active_tasks:
|
275
|
-
logger.debug(f"Cancelling {len(self._active_tasks)} active tasks...")
|
276
|
-
for task in self._active_tasks:
|
277
|
-
if not task.done():
|
278
|
-
task.cancel()
|
279
|
-
|
280
|
-
if self._active_tasks:
|
281
|
-
try:
|
282
|
-
await asyncio.wait_for(
|
283
|
-
asyncio.gather(*self._active_tasks, return_exceptions=True),
|
284
|
-
timeout=0.2
|
285
|
-
)
|
286
|
-
except asyncio.TimeoutError:
|
287
|
-
logger.debug("Some tasks did not complete in time")
|
288
|
-
|
289
|
-
# 清理ExecutorCore
|
290
|
-
await self.executor_core.cleanup()
|
291
|
-
|
292
|
-
# 清理event_pool
|
293
|
-
if hasattr(self.app.ep, 'cleanup'):
|
294
|
-
try:
|
295
|
-
self.app.ep.cleanup()
|
296
|
-
except Exception as e:
|
297
|
-
logger.error(f"Error cleaning up EventPool: {e}")
|
298
|
-
|
299
|
-
# 标记worker离线
|
300
|
-
if self.app.consumer_manager:
|
301
|
-
try:
|
302
|
-
self.app.consumer_manager.cleanup()
|
303
|
-
logger.debug("Worker marked as offline")
|
304
|
-
except Exception as e:
|
305
|
-
logger.error(f"Error marking worker offline: {e}")
|
306
|
-
|
307
|
-
logger.debug("UnifiedExecutor stopped")
|
308
|
-
|
309
|
-
def start_multi_process(self, queues: List[str], prefetch_multiplier: int = 100, worker_id: str = None, worker_key: str = None):
|
310
|
-
"""启动多进程模式
|
311
|
-
|
312
|
-
Args:
|
313
|
-
queues: 队列列表
|
314
|
-
prefetch_multiplier: 预取倍数
|
315
|
-
worker_id: Worker ID(主进程生成,子进程复用)
|
316
|
-
worker_key: Worker Key(主进程生成,子进程复用)
|
317
|
-
"""
|
318
|
-
if self.mode != ExecutionMode.MULTI_PROCESS:
|
319
|
-
raise RuntimeError("start_multi_process() is only for MULTI_PROCESS mode")
|
320
|
-
|
321
|
-
self.orchestrator.start(queues, prefetch_multiplier, worker_id, worker_key)
|
322
|
-
|
323
|
-
def shutdown(self):
|
324
|
-
"""
|
325
|
-
关闭执行器
|
326
|
-
|
327
|
-
根据执行模式调用相应的关闭方法
|
328
|
-
"""
|
329
|
-
if self.mode == ExecutionMode.MULTI_PROCESS:
|
330
|
-
if self.orchestrator:
|
331
|
-
self.orchestrator.shutdown()
|
332
|
-
elif self.mode == ExecutionMode.SINGLE_PROCESS:
|
333
|
-
# 单进程模式的清理在 _cleanup_single_process 中处理
|
334
|
-
# 这里只是一个占位符,实际清理由事件循环完成
|
335
|
-
logger.debug("UnifiedExecutor shutdown called in SINGLE_PROCESS mode")
|
336
|
-
|
337
|
-
|
338
|
-
__all__ = ['UnifiedExecutor']
|