django-simpletask5 0.2.8__tar.gz → 0.2.10__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.
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/PKG-INFO +117 -52
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/README.md +114 -48
- django_simpletask5-0.2.10/django_simpletask5/__init__.py +13 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/admin.py +153 -25
- django_simpletask5-0.2.10/django_simpletask5/apps.py +25 -0
- django_simpletask5-0.2.10/django_simpletask5/core/__init__.py +1 -0
- django_simpletask5-0.2.10/django_simpletask5/core/cronjob_registry.py +207 -0
- django_simpletask5-0.2.10/django_simpletask5/core/defaults.py +86 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/core/executor_scanner.py +47 -0
- django_simpletask5-0.2.10/django_simpletask5/core/lock.py +43 -0
- django_simpletask5-0.2.10/django_simpletask5/core/logging_utils.py +106 -0
- django_simpletask5-0.2.10/django_simpletask5/core/message_queue.py +269 -0
- django_simpletask5-0.2.10/django_simpletask5/core/publisher.py +372 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/core/reclaim.py +52 -5
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/core/security.py +10 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/core/signals.py +71 -4
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/core/worker_registry.py +80 -7
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/dashboards.py +116 -26
- django_simpletask5-0.2.10/django_simpletask5/executors/__init__.py +1 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/archive.py +15 -0
- django_simpletask5-0.2.10/django_simpletask5/executors/base.py +63 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/bash_script.py +22 -0
- django_simpletask5-0.2.10/django_simpletask5/executors/exceptions.py +14 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/loader.py +10 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/ping_pong.py +5 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/python_script.py +18 -0
- django_simpletask5-0.2.10/django_simpletask5/executors/retry_timeout.py +109 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/simple_request.py +17 -1
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/executors/status_check.py +8 -0
- django_simpletask5-0.2.10/django_simpletask5/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/locale/zh_Hans/LC_MESSAGES/django.po +19 -0
- django_simpletask5-0.2.10/django_simpletask5/management/__init__.py +1 -0
- django_simpletask5-0.2.10/django_simpletask5/management/commands/__init__.py +1 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/management/commands/django_simpletask_archive.py +16 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/management/commands/django_simpletask_crontab.py +69 -7
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/management/commands/django_simpletask_executor.py +130 -14
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/management/commands/django_simpletask_queues.py +22 -1
- django_simpletask5-0.2.10/django_simpletask5/management/commands/django_simpletask_sync_cronjobs.py +24 -0
- django_simpletask5-0.2.10/django_simpletask5/migrations/0014_cronjob_is_active_modified_by_user.py +22 -0
- django_simpletask5-0.2.10/django_simpletask5/migrations/0015_taskexecution_idx_exec_status_created.py +19 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/models.py +216 -11
- django_simpletask5-0.2.10/django_simpletask5/services/__init__.py +1 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/services/archive.py +62 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5.egg-info/PKG-INFO +117 -52
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5.egg-info/SOURCES.txt +2 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5.egg-info/requires.txt +1 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/pyproject.toml +3 -4
- django_simpletask5-0.2.8/django_simpletask5/__init__.py +0 -10
- django_simpletask5-0.2.8/django_simpletask5/apps.py +0 -13
- django_simpletask5-0.2.8/django_simpletask5/core/cronjob_registry.py +0 -130
- django_simpletask5-0.2.8/django_simpletask5/core/defaults.py +0 -39
- django_simpletask5-0.2.8/django_simpletask5/core/lock.py +0 -22
- django_simpletask5-0.2.8/django_simpletask5/core/logging_utils.py +0 -17
- django_simpletask5-0.2.8/django_simpletask5/core/message_queue.py +0 -143
- django_simpletask5-0.2.8/django_simpletask5/core/publisher.py +0 -192
- django_simpletask5-0.2.8/django_simpletask5/executors/__init__.py +0 -0
- django_simpletask5-0.2.8/django_simpletask5/executors/base.py +0 -35
- django_simpletask5-0.2.8/django_simpletask5/executors/exceptions.py +0 -13
- django_simpletask5-0.2.8/django_simpletask5/executors/retry_timeout.py +0 -65
- django_simpletask5-0.2.8/django_simpletask5/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- django_simpletask5-0.2.8/django_simpletask5/management/__init__.py +0 -0
- django_simpletask5-0.2.8/django_simpletask5/management/commands/__init__.py +0 -0
- django_simpletask5-0.2.8/django_simpletask5/management/commands/django_simpletask_sync_cronjobs.py +0 -14
- django_simpletask5-0.2.8/django_simpletask5/migrations/__init__.py +0 -0
- django_simpletask5-0.2.8/django_simpletask5/services/__init__.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/LICENSE +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0001_initial.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0002_taskexecution_done_tasks_count_and_more.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0003_alter_cronjob_options_remove_cronjob_name_and_more.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0004_alter_cronjob_is_modified_by_user.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0005_alter_cronjob_is_modified_by_user_help.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0006_alter_cronjob_options_alter_taskexecution_created_at_and_more.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0007_rename_task_model_to_executor_class_in_stat.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0008_taskexecution_pickup_timeout_seconds.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0009_add_cronjob_app_label.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0010_taskexecution_id_alter_taskexecution_execution_id.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0011_taskexecutionstat_canceled_count.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0012_alter_taskexecution_status.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/migrations/0013_alter_taskexecution_task_id.py +0 -0
- {django_simpletask5-0.2.8/django_simpletask5/core → django_simpletask5-0.2.10/django_simpletask5/migrations}/__init__.py +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/static/django_simpletask5/css/dashboard.css +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5/static/django_simpletask5/js/select2_executor.js +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5.egg-info/dependency_links.txt +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/django_simpletask5.egg-info/top_level.txt +0 -0
- {django_simpletask5-0.2.8 → django_simpletask5-0.2.10}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-simpletask5
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.10
|
|
4
4
|
Summary: Lightweight async task execution framework for Django
|
|
5
5
|
Author-email: rRR0VrFP <rrr0vrfp@qq.com>
|
|
6
6
|
Maintainer-email: rRR0VrFP <rrr0vrfp@qq.com>
|
|
@@ -15,14 +15,13 @@ Classifier: Framework :: Django :: 5.1
|
|
|
15
15
|
Classifier: Framework :: Django :: 5.2
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.10
|
|
24
22
|
Description-Content-Type: text/markdown
|
|
25
23
|
License-File: LICENSE
|
|
24
|
+
Requires-Dist: Django>=4.2
|
|
26
25
|
Requires-Dist: globallock>=0.2.0
|
|
27
26
|
Requires-Dist: django-safe-fields>=0.2.3
|
|
28
27
|
Requires-Dist: django-app-requires>=0.3.6
|
|
@@ -51,7 +50,7 @@ Dynamic: license-file
|
|
|
51
50
|
- **自定义事件** — 通过 `task.trigger('event_name')` 触发任意事件
|
|
52
51
|
- **灵活的执行器映射** — 不同事件可绑定不同的执行器类
|
|
53
52
|
- **队列路由** — 不同事件可路由到不同优先级队列
|
|
54
|
-
- **重试与超时** —
|
|
53
|
+
- **重试与超时** — 失败自动重试(指数退避),支持 Pickup 超时检测;运行中孤儿任务由执行锁回收
|
|
55
54
|
- **拒绝回退** — 执行器可通过 `RejectException` 拒绝当前任务,消息自动重新入队等待下次调度
|
|
56
55
|
- **加密字段** — 敏感数据自动加密存储
|
|
57
56
|
- **Cron 调度** — 内置 crontab 守护进程,支持代码注册与数据库覆盖
|
|
@@ -61,10 +60,10 @@ Dynamic: license-file
|
|
|
61
60
|
|
|
62
61
|
## 依赖
|
|
63
62
|
|
|
64
|
-
- Python >= 3.
|
|
65
|
-
- Django >=
|
|
66
|
-
- Kombu >= 5.
|
|
67
|
-
- Redis >= 4.0
|
|
63
|
+
- Python >= 3.10
|
|
64
|
+
- Django >= 4.2(兼容至 5.2.x)
|
|
65
|
+
- Kombu >= 5.6.2(消息队列,支持 RabbitMQ/Redis/内存)
|
|
66
|
+
- Redis >= 7.4.0(分布式锁、Worker 注册中心)
|
|
68
67
|
- 详见 `pyproject.toml`
|
|
69
68
|
|
|
70
69
|
## 安装
|
|
@@ -134,6 +133,8 @@ INSTALLED_APPS = [
|
|
|
134
133
|
# ── 消息队列(MQ)────────────────────────────────────────────
|
|
135
134
|
# 生产使用 RabbitMQ 或 Redis;开发调试可用内存队列
|
|
136
135
|
DJANGO_SIMPLETASK_BROKER_URL = 'amqp://guest:guest@127.0.0.1:5672//'
|
|
136
|
+
DJANGO_SIMPLETASK_BROKER_HEARTBEAT = 60 # 发布连接池的 broker 心跳(秒)
|
|
137
|
+
DJANGO_SIMPLETASK_MAX_REQUEUE_DELIVERIES = 100 # AMQP 消息最大重投次数,超过后丢弃
|
|
137
138
|
# 若不设 BROKER_URL,则按以下参数组合:
|
|
138
139
|
# DJANGO_SIMPLETASK_MESSAGE_QUEUE_TYPE = 'rabbitmq' # 'memory' | 'rabbitmq' | 'redis'
|
|
139
140
|
# DJANGO_SIMPLETASK_RABBITMQ_HOST = '127.0.0.1'
|
|
@@ -172,16 +173,28 @@ DJANGO_SIMPLETASK_HIGH_PRIORITY_QUEUE = 'myproject:django-simpletask5:queue:high
|
|
|
172
173
|
|
|
173
174
|
# ── 执行与重试 ───────────────────────────────────────────────
|
|
174
175
|
DJANGO_SIMPLETASK_DEFAULT_MAX_RETRIES = 3
|
|
175
|
-
DJANGO_SIMPLETASK_DEFAULT_TIMEOUT_SECONDS = 600 #
|
|
176
|
+
DJANGO_SIMPLETASK_DEFAULT_TIMEOUT_SECONDS = 600 # 执行超时字段(秒,当前不主动中断)
|
|
176
177
|
DJANGO_SIMPLETASK_DEFAULT_PICKUP_TIMEOUT_SECONDS = 3600 # Pickup 超时(秒)
|
|
177
178
|
|
|
179
|
+
# ── 发布重试 ────────────────────────────────────────────────
|
|
180
|
+
DJANGO_SIMPLETASK_PUBLISH_RETRY_POLICY = {
|
|
181
|
+
'max_retries': 2,
|
|
182
|
+
'interval_start': 0.2,
|
|
183
|
+
'interval_step': 0.3,
|
|
184
|
+
'interval_max': 1.0,
|
|
185
|
+
}
|
|
186
|
+
DJANGO_SIMPLETASK_PUBLISH_RETRY_BASE_DELAY = 1.0 # 应用层第 1 次重试等待(秒)
|
|
187
|
+
DJANGO_SIMPLETASK_PUBLISH_RETRY_MAX_DELAY = 5.0 # 应用层重试等待上限(秒)
|
|
188
|
+
|
|
178
189
|
# ── Worker / 日志运维参数 ────────────────────────────────────
|
|
179
|
-
DJANGO_SIMPLETASK_WORKER_HEARTBEAT_INTERVAL = 60 # Worker
|
|
180
|
-
DJANGO_SIMPLETASK_SHUTDOWN_TIMEOUT = 30 #
|
|
181
|
-
DJANGO_SIMPLETASK_LOG_INTERVAL = 300 #
|
|
190
|
+
DJANGO_SIMPLETASK_WORKER_HEARTBEAT_INTERVAL = 60 # Worker 心跳/巡检间隔(秒)
|
|
191
|
+
DJANGO_SIMPLETASK_SHUTDOWN_TIMEOUT = 30 # 每个 worker 线程的优雅停机等待(秒)
|
|
192
|
+
DJANGO_SIMPLETASK_LOG_INTERVAL = 300 # 存活/注册失败等日志节流间隔(秒)
|
|
193
|
+
DJANGO_SIMPLETASK_PUBLISH_LOG_INTERVAL = 60 # 发布/回收失败日志节流间隔(秒)
|
|
182
194
|
|
|
183
195
|
# ── 归档与统计 ──────────────────────────────────────────────
|
|
184
|
-
|
|
196
|
+
# 归档文件通过 TaskExecutionArchive.file 写入 MEDIA_ROOT/django_simpletask5_archives/%Y/%m/
|
|
197
|
+
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
|
185
198
|
DJANGO_SIMPLETASK_ARCHIVE_SALT = 'change-me-archive-salt' # 归档加密盐值
|
|
186
199
|
DJANGO_SIMPLETASK_ARCHIVE_RETENTION_DAYS = 7
|
|
187
200
|
|
|
@@ -214,18 +227,24 @@ django_app_requires_patch_all()
|
|
|
214
227
|
| `DJANGO_SIMPLETASK_MESSAGE_QUEUE_TYPE` | `'memory'` | `memory` 仅用于开发调试;**生产必须改为 `rabbitmq` 或 `redis`**,否则消息不持久化、跨进程 Worker 无法消费 |
|
|
215
228
|
| `DJANGO_SIMPLETASK_RABBITMQ_HOST/PORT/USER/PASSWORD` | `127.0.0.1 / 5672 / guest / guest` | 类型为 `rabbitmq` 且未设 `BROKER_URL` 时生效。生产务必更换默认 `guest` 凭据 |
|
|
216
229
|
| `DJANGO_SIMPLETASK_REDIS_MQ_HOST/PORT/DB/PASSWORD` | `127.0.0.1 / 6379 / 0 / None` | 类型为 `redis` 时生效。建议与锁/缓存使用**不同 db**,避免 key 冲突与互相阻塞。Redis 需认证时设置 `PASSWORD` |
|
|
230
|
+
| `DJANGO_SIMPLETASK_BROKER_HEARTBEAT` | `60` | Kombu 发布连接池的 broker heartbeat(秒)。网络中间设备会断开空闲连接时建议保持开启 |
|
|
231
|
+
| `DJANGO_SIMPLETASK_MAX_REQUEUE_DELIVERIES` | `100` | AMQP 消息被 reject/requeue 的最大次数,超过后 ack 丢弃,防止毒消息无限循环;memory/redis 传输无 `x-death` 时不生效 |
|
|
217
232
|
| `CACHES['default']` | — | 默认锁引擎 `DjangoRedisGlobalLock` 依赖它,**必须配置为 Redis 缓存** |
|
|
218
233
|
| `DJANGO_SIMPLETASK_LOCK_CONFIG` | `None`(默认复用缓存锁) | 需要独立连接参数时显式配置(`global_lock_engine_options` 支持 `host/port/db/password`)。**注意**:Worker 注册中心(心跳/状态)也读取该配置的 `host/port/db/password`,未配置时固定连 `127.0.0.1:6379/0`。globallock>=0.2.0 起锁有效期由 lockman/lock 作用域管理(内置看门狗自动续期),**不再需要配置 `LOCK_TIMEOUT` / `LOCK_RENEW_INTERVAL`** |
|
|
219
234
|
| `DJANGO_SIMPLETASK_KEY_PREFIX` | 自动取 `ROOT_URLCONF` 首段 | 生产建议**显式指定**,多项目共用 Redis 时避免 key 冲突 |
|
|
220
235
|
| `DJANGO_SIMPLETASK_DEFAULT_QUEUE` | `{prefix}:...:queue:default` | 默认队列名。多环境(dev/staging/prod)建议加环境后缀隔离 |
|
|
221
236
|
| `DJANGO_SIMPLETASK_HIGH_PRIORITY_QUEUE` | `{prefix}:...:queue:high_priority` | 高优先级队列,**需单独启动 Worker 消费** |
|
|
222
237
|
| `DJANGO_SIMPLETASK_DEFAULT_MAX_RETRIES` | `3` | 默认最大重试次数。执行器幂等性好的任务可加大 |
|
|
223
|
-
| `DJANGO_SIMPLETASK_DEFAULT_TIMEOUT_SECONDS` | `600` |
|
|
238
|
+
| `DJANGO_SIMPLETASK_DEFAULT_TIMEOUT_SECONDS` | `600` | 写入 `TaskExecution.timeout_seconds`,并用于计算 running 时的 `expire_time`。当前版本不会因此主动中断 running 任务;严格超时应由执行器自行实现 |
|
|
224
239
|
| `DJANGO_SIMPLETASK_DEFAULT_PICKUP_TIMEOUT_SECONDS` | `3600` | Pickup 超时(等待 Worker 消费的上限),**通常大于执行超时**,因为排队等待时间不可控 |
|
|
225
|
-
| `
|
|
226
|
-
| `
|
|
227
|
-
| `
|
|
228
|
-
| `
|
|
240
|
+
| `DJANGO_SIMPLETASK_PUBLISH_RETRY_POLICY` | `{'max_retries': 2, 'interval_start': 0.2, 'interval_step': 0.3, 'interval_max': 1.0}` | Kombu Producer 内层重试策略。应用层还会做最多 3 次发布尝试 |
|
|
241
|
+
| `DJANGO_SIMPLETASK_PUBLISH_RETRY_BASE_DELAY` | `1.0` | 应用层发布重试第 1 次等待(秒),之后按指数退避 |
|
|
242
|
+
| `DJANGO_SIMPLETASK_PUBLISH_RETRY_MAX_DELAY` | `5.0` | 应用层单次发布重试等待上限(秒) |
|
|
243
|
+
| `DJANGO_SIMPLETASK_WORKER_HEARTBEAT_INTERVAL` | `60` | Worker 心跳/巡检间隔,应**小于 Worker 注册 TTL(300 秒)**,否则注册状态被误清理 |
|
|
244
|
+
| `DJANGO_SIMPLETASK_SHUTDOWN_TIMEOUT` | `30` | **每个** worker 线程的优雅停机等待时长;总等待时间可能为 worker 数乘以该值 |
|
|
245
|
+
| `DJANGO_SIMPLETASK_LOG_INTERVAL` | `300` | Worker 注册失败、Crontab 错误等日志的节流间隔。日志量大时可调大 |
|
|
246
|
+
| `DJANGO_SIMPLETASK_PUBLISH_LOG_INTERVAL` | `60` | 发布、重投、孤儿回收失败日志的节流间隔(秒) |
|
|
247
|
+
| `DJANGO_SIMPLETASK_ARCHIVE_PATH` | `django_simpletask5_archives` | **当前版本未读取,仅保留兼容**。归档文件实际写入 `MEDIA_ROOT/django_simpletask5_archives/%Y/%m/` |
|
|
229
248
|
| `DJANGO_SIMPLETASK_ARCHIVE_SALT` | `django-simpletask5-archive` | 归档加密盐值,**生产必须修改**(结合 `SECRET_KEY` 派生 AES 密钥) |
|
|
230
249
|
| `DJANGO_SIMPLETASK_ARCHIVE_RETENTION_DAYS` | `7` | 归档保留天数,按合规要求调整 |
|
|
231
250
|
| `DJANGO_SIMPLETASK_CRONJOB_AUTO_SYNC` | `True` | 自动把代码注册的 Cron 同步到数据库;只想手工管理时设为 `False` |
|
|
@@ -238,6 +257,8 @@ django_app_requires_patch_all()
|
|
|
238
257
|
|
|
239
258
|
> 注意:`DJANGO_SIMPLETASK_ENABLE_SCRIPT_EXECUTORS` 设为 `True` 后,还需为用户/组授予 `django_simpletask5 | Cron job | Can use script executors` 权限,非超级管理员无法创建 Python/Shell 脚本执行器类型的定时任务。
|
|
240
259
|
|
|
260
|
+
> 发布可靠性说明:应用层 `_publish_message` 最多尝试 3 次(首次 + 2 次重试),失败等待按 `PUBLISH_RETRY_BASE_DELAY` 指数增长并受 `PUBLISH_RETRY_MAX_DELAY` 限制;Kombu Producer 内部还会按 `PUBLISH_RETRY_POLICY` 重试。最终仍失败时,`safe_publish_message()` 会把非终态 execution 标记为 `failed`,不会让调用方因 broker 异常崩溃。`publish_message()` 保留异常上抛语义,供需要自行处理错误的调用方使用。
|
|
261
|
+
|
|
241
262
|
## 快速开始(从零到一集成)
|
|
242
263
|
|
|
243
264
|
本框架的集成分三步:**定义任务模型 → 编写执行器 → 启动 Worker**。完整可运行的参考实现见仓库中的 `django_simpletask5_example` 应用,包含 `OrderTask` 模型、创建/更新/删除/退款四个执行器及内置 Cron 任务。
|
|
@@ -292,7 +313,7 @@ from django_simpletask5.executors.base import BaseExecutor
|
|
|
292
313
|
from django_simpletask5.models import TaskExecution
|
|
293
314
|
|
|
294
315
|
class OrderCreateExecutor(BaseExecutor):
|
|
295
|
-
timeout_seconds = 300 #
|
|
316
|
+
timeout_seconds = 300 # 记录预期执行上限(当前不主动中断,可选)
|
|
296
317
|
pickup_timeout_seconds = 1800 # Pickup 超时 30 分钟(可选)
|
|
297
318
|
|
|
298
319
|
def execute(self, execution: TaskExecution, task=None) -> str | None:
|
|
@@ -486,7 +507,7 @@ OrderTask.objects.create(..., skip_publish=True) # 方式四:构造参数(
|
|
|
486
507
|
| `get_tasks(execution)` | 返回可迭代对象,默认 `yield None`(单任务) |
|
|
487
508
|
| `get_tasks_count(execution)` | 任务总数(用于进度展示),默认 `1` |
|
|
488
509
|
| `reject(execution, reason='')` | 拒绝任务:状态复位 `pending`、清空 `started_at`、刷新 `expire_time` |
|
|
489
|
-
| `timeout_seconds` / `pickup_timeout_seconds` |
|
|
510
|
+
| `timeout_seconds` / `pickup_timeout_seconds` | 类属性。`pickup_timeout_seconds` 用于自动超时判定;`timeout_seconds` 当前仅记录预期执行上限,不主动中断任务 |
|
|
490
511
|
| `get_parameter_schema()` | 声明执行器参数,用于 Admin 参数配置界面(默认空) |
|
|
491
512
|
|
|
492
513
|
### execute 签名与返回值
|
|
@@ -558,12 +579,14 @@ class MyExecutor(BaseExecutor):
|
|
|
558
579
|
|
|
559
580
|
```python
|
|
560
581
|
class OrderCreateExecutor(BaseExecutor):
|
|
561
|
-
timeout_seconds = 300 #
|
|
582
|
+
timeout_seconds = 300 # 记录预期执行上限(当前不主动中断)
|
|
562
583
|
pickup_timeout_seconds = 1800 # Pickup 超时 30 分钟
|
|
563
584
|
```
|
|
564
585
|
|
|
565
586
|
未配置时使用全局默认值 `DJANGO_SIMPLETASK_DEFAULT_TIMEOUT_SECONDS`(600 秒)与 `DJANGO_SIMPLETASK_DEFAULT_PICKUP_TIMEOUT_SECONDS`(3600 秒)。
|
|
566
587
|
|
|
588
|
+
> 注意:当前版本只有 `pickup_timeout_seconds` 会被框架自动判定并触发 `timeout`;`timeout_seconds` 不会中止正在运行的执行器。需要严格执行时限时,请在 `execute()` 内部自行实现超时或使用可取消的子进程。
|
|
589
|
+
|
|
567
590
|
## 模型与生命周期
|
|
568
591
|
|
|
569
592
|
### Task 抽象模型
|
|
@@ -608,21 +631,23 @@ class OrderTask(Task):
|
|
|
608
631
|
|
|
609
632
|
```
|
|
610
633
|
pending ──(Worker 消费)──▶ running ──(执行成功)──▶ success
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
634
|
+
│ │
|
|
635
|
+
│ ├─(异常且可重试)────────▶ retry ──(重新入队并被消费)──▶ running
|
|
636
|
+
│ ├─(异常且重试耗尽)──────▶ failed
|
|
637
|
+
│ └─(执行器异常退出/锁丢失)─▶ retry 或 failed(由 reclaim 回收)
|
|
638
|
+
├─(pickup 超时,从未被领取)──▶ timeout ──(RetryTimeout)──▶ retry
|
|
639
|
+
├─(用户取消)────────────────▶ canceled(终态)
|
|
640
|
+
└─(发布最终失败)────────────▶ failed
|
|
616
641
|
```
|
|
617
642
|
|
|
618
643
|
| 状态 | 含义 |
|
|
619
644
|
|---|---|
|
|
620
|
-
| `pending` | 已发布,等待 Worker
|
|
621
|
-
| `running` |
|
|
645
|
+
| `pending` | 已发布,等待 Worker 消费;拒绝回退也会回到该状态 |
|
|
646
|
+
| `running` | 已获取执行锁,正在执行 |
|
|
622
647
|
| `success` | 执行成功 |
|
|
623
|
-
| `failed` |
|
|
624
|
-
| `retry` |
|
|
625
|
-
| `timeout` | 超过 `expire_time`
|
|
648
|
+
| `failed` | 重试次数耗尽、发布最终失败,或孤儿回收时已无重试次数 |
|
|
649
|
+
| `retry` | 失败/超时后等待重新投递,消息已重新入队 |
|
|
650
|
+
| `timeout` | **pending** 超过 pickup 期限仍未被领取;running 不会因 `expire_time` 自动变成 timeout |
|
|
626
651
|
| `canceled` | 被用户主动取消(终态,Worker 不会执行) |
|
|
627
652
|
|
|
628
653
|
### 取消任务
|
|
@@ -642,29 +667,29 @@ TaskExecution.objects.filter(
|
|
|
642
667
|
### 重试与超时
|
|
643
668
|
|
|
644
669
|
- **重试**:`max_retries`(默认 3)控制最大重试次数,`retry_count` 累计已重试次数;重试消息重新发布到原队列
|
|
645
|
-
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
- **超时检测与恢复**:内置 `StatusCheckExecutor`(内置 Cron)将超时的 `pending`/`running` 记录标记为 `timeout`;`RetryTimeoutExecutor` 将 `timeout` 记录重新入队重试(执行超时消耗重试次数,Pickup 超时不消耗)
|
|
670
|
+
- **Pickup 超时**(`pickup_timeout_seconds`):**唯一由框架自动判定的超时**。`pending` 超过 `expire_time` 后由 `StatusCheckExecutor` 标记为 `timeout`,也用于拒绝回退后的重新调度保护;Pickup 超时不消耗重试次数
|
|
671
|
+
- **执行超时字段**(`timeout_seconds`):写入 `TaskExecution.timeout_seconds`,并在任务进入 `running` 时用于计算 `expire_time`。当前版本**不会**因该时间到达而主动中断 running 任务;如果执行器 hang 住,需在执行器内部实现超时/取消机制
|
|
672
|
+
- **超时检测与恢复**:`StatusCheckExecutor` 每分钟把超过 pickup 期限的 `pending` 记录标记为 `timeout`;`RetryTimeoutExecutor` 每分钟把 `timeout` 记录重新入队,实际运行过的记录消耗一次重试次数。running 孤儿任务由 crontab 基于执行锁过期回收,而不是按 `expire_time` 判超时
|
|
649
673
|
|
|
650
674
|
### 其他模型
|
|
651
675
|
|
|
652
676
|
- `CronJob` — 定时任务注册表,支持代码注册与数据库覆盖(见「Cron 任务」)
|
|
653
|
-
- `TaskExecutionArchive` —
|
|
654
|
-
- `TaskExecutionStat` —
|
|
677
|
+
- `TaskExecutionArchive` — 归档记录,终态执行记录归档为加密 JSONL(见配置「归档」)
|
|
678
|
+
- `TaskExecutionStat` — 按日期、`executor_class`、`trigger_event` 聚合的日统计
|
|
655
679
|
|
|
656
680
|
## Cron 任务
|
|
657
681
|
|
|
658
682
|
### 在代码中定义 Cron 任务
|
|
659
683
|
|
|
660
|
-
在任意 app 的 `cronjobs.py` 文件中使用 `register_cronjob`
|
|
684
|
+
在任意 app 的 `cronjobs.py` 文件中使用 `register_cronjob` 注册,并在该 app 的 `AppConfig.ready()` 中导入这个模块;框架不会自动发现各 app 下的 `cronjobs.py`:
|
|
661
685
|
|
|
662
686
|
```python
|
|
663
687
|
# myapp/cronjobs.py
|
|
664
|
-
from django_simpletask5.cronjob_registry import register_cronjob
|
|
688
|
+
from django_simpletask5.core.cronjob_registry import register_cronjob
|
|
665
689
|
|
|
666
690
|
register_cronjob(
|
|
667
|
-
|
|
691
|
+
uid='health_check',
|
|
692
|
+
display_name='Health Check',
|
|
668
693
|
cron_expression='*/5 * * * *',
|
|
669
694
|
executor_class='django_simpletask5.executors.simple_request.SimpleRequestExecutor',
|
|
670
695
|
context={
|
|
@@ -676,15 +701,33 @@ register_cronjob(
|
|
|
676
701
|
)
|
|
677
702
|
```
|
|
678
703
|
|
|
704
|
+
```python
|
|
705
|
+
# myapp/apps.py
|
|
706
|
+
from django.apps import AppConfig
|
|
707
|
+
|
|
708
|
+
class MyAppConfig(AppConfig):
|
|
709
|
+
name = 'myapp'
|
|
710
|
+
|
|
711
|
+
def ready(self):
|
|
712
|
+
import myapp.cronjobs # noqa: F401
|
|
713
|
+
```
|
|
714
|
+
|
|
679
715
|
### 从代码同步到数据库
|
|
680
716
|
|
|
681
|
-
注册的 Cron
|
|
717
|
+
注册的 Cron 任务需要同步到数据库才会生效。`django_simpletask_crontab` 运行期间每个 tick 都会自动同步(默认开启,可通过 `DJANGO_SIMPLETASK_CRONJOB_AUTO_SYNC = False` 关闭自动同步),也可以手动执行:
|
|
682
718
|
|
|
683
719
|
```bash
|
|
684
720
|
python manage.py django_simpletask_sync_cronjobs
|
|
685
721
|
```
|
|
686
722
|
|
|
687
|
-
|
|
723
|
+
手动同步命令使用 `sync_cronjobs(force=True)`,即使关闭了自动同步也会执行。同步后,用户可以在 Django Admin 中查看和修改 Cron 任务,被手动修改过的任务不会在后续同步中被覆盖(`is_modified_by_user` 标记保护)。单个 CronJob 创建失败只记录日志,不会中断其它任务同步。
|
|
724
|
+
|
|
725
|
+
启用/停用(`is_active`)的所有权独立跟踪,规则与 `is_modified_by_user` 对称——**手动改过就一直归你,直到显式重置**:
|
|
726
|
+
|
|
727
|
+
- 用户**未**手动启停过的任务,`is_active` 会按代码注册值同步(例如 `simpletask_ping_pong` 注册为 `is_active=False`,默认保持停用)。
|
|
728
|
+
- 用户手动暂停/启用后,会置 `is_active_modified_by_user` 标记,自动同步不再覆盖该状态,但**不影响**其它定义字段(`cron_expression` 等仍照常同步)。
|
|
729
|
+
- 该标记不会自动清除;需要让它重新跟随代码时,使用管理后台的“从代码定义重置”(`reset_from_code`),或在编辑页取消勾选“保留手动启用状态”。
|
|
730
|
+
- 这样手动启停的状态可稳定保持,不会因代码默认值变化而悄悄被改写。
|
|
688
731
|
|
|
689
732
|
> 注意:cron 表达式按 Django 的 `TIME_ZONE` 设置对应的本地时间解释。`USE_TZ=True` 时仍以本地时间计算触发时刻(存储为 UTC),例如 `TIME_ZONE = 'Asia/Shanghai'` 下 `30 14 * * *` 表示每天 14:30 本地时间触发。
|
|
690
733
|
|
|
@@ -692,13 +735,14 @@ python manage.py django_simpletask_sync_cronjobs
|
|
|
692
735
|
|
|
693
736
|
| 执行器 | 说明 |
|
|
694
737
|
|---|---|
|
|
695
|
-
| `PingPongExecutor` | 健康检查,返回 `'pong'
|
|
738
|
+
| `PingPongExecutor` | 健康检查,返回 `'pong'`;内置 Cron 默认停用 |
|
|
696
739
|
| `BashScriptExecutor` | 执行 Shell 脚本 |
|
|
697
740
|
| `PythonScriptExecutor` | 执行 Python 代码 |
|
|
698
741
|
| `SimpleRequestExecutor` | 发起 HTTP 请求 |
|
|
699
|
-
| `StatusCheckExecutor` |
|
|
700
|
-
| `RetryTimeoutExecutor` |
|
|
701
|
-
| `ArchiveExecutor` |
|
|
742
|
+
| `StatusCheckExecutor` | 检测超过 pickup 期限、从未被领取的 `pending` 记录并标记 `timeout`(内置 Cron 每分钟) |
|
|
743
|
+
| `RetryTimeoutExecutor` | 将 `timeout` 记录重新入队(内置 Cron 每分钟;运行过的记录消耗一次重试) |
|
|
744
|
+
| `ArchiveExecutor` | 归档前一天的终态执行并生成统计(内置 Cron 每天 2:00) |
|
|
745
|
+
| `PurgeExpiredExecutor` | 删除超过 `ARCHIVE_RETENTION_DAYS` 的终态执行记录(内置 Cron 每天 3:00;不删除归档文件) |
|
|
702
746
|
|
|
703
747
|
## 开发与测试
|
|
704
748
|
|
|
@@ -732,6 +776,32 @@ Worker 消费消息 → 获取分布式锁 → 加载执行器 → 执行并保
|
|
|
732
776
|
|
|
733
777
|
## Releases
|
|
734
778
|
|
|
779
|
+
### 0.2.10
|
|
780
|
+
|
|
781
|
+
- **修复 MySQL/InnoDB 并发写入死锁** — 后台批量改状态原使用 `WHERE execution_id IN (...)`、`WHERE status = 'pending'` 等**二级索引范围更新**,会与前台 `Task` 子类 `create()` 触发的 `TaskExecution` 插入在二级索引间隙上相互阻塞,在高并发下偶发 `OperationalError (1213)` 死锁。现改为**先查出主键、再按主键更新**(`WHERE id IN (...)`),不触碰二级索引间隙,从源头消除死锁,无需重试、不改变原有同事务语义
|
|
782
|
+
- `django_simpletask5/executors/retry_timeout.py`:领取超时 / 执行超时的批量改状态改为按主键更新
|
|
783
|
+
- `django_simpletask5/admin.py`:`cancel_selected` 批量取消改为按主键更新
|
|
784
|
+
- **修复 MySQL 下 `bulk_create` 触发任务时 `task_id` 为空** — 部分数据库后端(如 MySQL)的 `bulk_create` 不回填自增主键,导致批量创建时生成的 `TaskExecution.task_id` 落成 `None`,业务对象无法反查。现按 `skip_publish` 分流:标记跳过发布的实例仍走高性能批量插入,需要触发任务的实例改为逐条保存,由信号在拿到真实主键后创建并发布 execution。返回值语义保持不变:仍返回传入的同一批实例并保持原顺序
|
|
785
|
+
- **修复示例项目 `OrderTask` 迁移的主键顺序** — 修正 `0002` 迁移先删旧主键再新增自增主键,解决 MySQL 执行时 `1068 Multiple primary key defined`
|
|
786
|
+
- **Dashboard 查询优化** — 卡片当日指标、近 30 天状态分布改用走索引的自然日范围查询(`created_at__gte/__lt`)替代 `created_at__date` 全表扫描;每日执行图表由逐日 60 次 `count` 合并为单次 `TruncDate` 分组聚合。仪表盘整体查询数降至常量级,不再随天数增长。新增 `TaskExecution` 的 `(status, created_at)` 联合索引(迁移 `0015`)支撑状态分布查询
|
|
787
|
+
- **Dashboard Worker 表增强** — Active Workers 表新增“运行时长”与“最后心跳”两列;心跳距今超过 Worker 注册 TTL 阈值(约 60%)时以 `⚠` 标记,提示该 Worker 可能即将失联;时间戳缺失或非法时降级为 `-`,不影响其余展示
|
|
788
|
+
|
|
789
|
+
### 0.2.9
|
|
790
|
+
|
|
791
|
+
- **启用状态所有权独立跟踪** — 新增 `CronJob.is_active_modified_by_user` 字段与迁移 `0014`,将“启用/暂停状态”与“定义字段”的所有权彻底分离。用户手动启停(快捷按钮 / 批量操作 / 编辑表单)后置该标记,自动同步(`sync_cronjobs`)不再覆盖 `is_active`;未手动改过的任务仍按代码注册值同步。暂停只锁 `is_active`,`cron_expression` 等定义字段照常同步
|
|
792
|
+
- **显式释放所有权** — 该标记不会自动清除;需要让任务重新跟随代码时,使用管理后台“从代码定义重置”(`reset_from_code`),或在编辑页取消勾选“保留手动启用状态”
|
|
793
|
+
- **保留列表分页/过滤条件** — 修复暂停/运行按钮提交后重定向丢失查询串的问题:按钮表单 `action` 及视图重定向均携带当前 changelist 的 `GET` 参数,操作后刷新仍停留在原分页、过滤器与搜索条件
|
|
794
|
+
- **消息发布可靠性增强** — Kombu 发布复用连接池并支持 broker heartbeat;`publish()` 返回 `queue` / `message_count` / `consumer_count` 队列统计;新增可配置发布重试策略和递增重试延迟;发布异常时自动丢弃损坏连接;新增公开的 `publish_message()` 与兜底的 `safe_publish_message()`,最终发布失败会将 execution 标记为 `failed`
|
|
795
|
+
- **发布日志节流** — 发布、重试投递、孤儿任务回收和 CronJob 触发失败统一接入日志节流,避免 broker/Redis 长时间不可用时刷屏;重投日志同时记录队列积压与消费者数量
|
|
796
|
+
- **Worker 与回收健壮性** — Worker 注册表 Redis 客户端改为公开 `get_redis_client()`;修正 Worker 清理 key 的 glob 为 `worker:{pid}:*`;`reclaim._lock_config()` 与 `get_lock_manager()` 对齐,只有 `None` 才回退默认锁配置;回收扫描前后关闭旧数据库连接,减少长时间运行后的连接失效
|
|
797
|
+
- **日志初始化修复** — `setup_console_logging()` 不再把 `FileHandler` 误判为控制台 handler,避免已有文件日志时跳过控制台输出
|
|
798
|
+
- **CronJob 同步健壮性** — `sync_cronjobs(force=True)` 支持手动同步命令绕过 `DJANGO_SIMPLETASK_CRONJOB_AUTO_SYNC`;单条 CronJob 创建失败不再中断其它任务同步;`django_simpletask_sync_cronjobs` 命令改为强制同步
|
|
799
|
+
- **API 命名整理** — `_CRONJOB_REGISTRY` → `CRONJOB_REGISTRY`、`_compute_next_run_time()` → `compute_next_run_time()`、`_get_broker_url()` → `get_broker_url()`、`_model_to_dict()` → `model_to_dict()`、`_DeleteIntercepted` → `DeleteIntercepted`、`_get_redis_client()` → `get_redis_client()`;依赖旧私有名称的扩展或测试需同步调整
|
|
800
|
+
- **可观测性入口公开** — Dashboard 使用的 Worker 统计入口由 `_get_worker_stats` 改为公开的 `get_worker_stats()`;消费者线程心跳改为读取 `DJANGO_SIMPLETASK_WORKER_HEARTBEAT_INTERVAL`,Worker 注册、Redis 降级和日志节流逻辑补充了完整说明
|
|
801
|
+
- **注释与文档校准** — 为整个 `django_simpletask5` 生产包补充中文模块/类/函数/分支注释,并修正归档路径、归档清理、`running/timeout` 状态流转、Admin 只读性、脚本白名单、连接池 heartbeat、优雅退出等待时间等与实际行为不一致的说明
|
|
802
|
+
- **迁移与翻译** — 新增迁移 `django_simpletask5.0014_cronjob_is_active_modified_by_user`,同步更新简体中文翻译与迁移中的字段 `help_text`
|
|
803
|
+
- **环境要求** — 代码使用 `X | None` 联合类型,最低 Python 版本明确为 3.10;同时将 Django 最低版本明确为 4.2,并补充 `Django>=4.2` 依赖声明
|
|
804
|
+
|
|
735
805
|
### 0.2.8
|
|
736
806
|
|
|
737
807
|
- **移除 `Task` 冗余的 `task_id` 字段** — `TaskExecution.task_id` 由 UUID 改为 `CharField(max_length=255)`,直接用于存放业务对象主键字符串(`str(instance.pk)`),不再维护一份与业务主键重复的 UUID。消息上下文中的 `task_id` 字段名不变,值语义从 UUID 变为业务主键字符串(如 `"42"`)
|
|
@@ -745,15 +815,11 @@ Worker 消费消息 → 获取分布式锁 → 加载执行器 → 执行并保
|
|
|
745
815
|
- **重试不再搁浅任务** — 失败重试先落库 `retry` 再发布重试消息,发布失败则 requeue 当前消息,杜绝任务永久停在无人处理的 `retry` 状态;KombuQueue 依据 AMQP `x-death` 上限丢弃毒消息,避免无限 requeue 造成的队头阻塞与空转
|
|
746
816
|
- **消费者重连提示** — Worker 断连重连成功后输出 `Consumer connection restored` 恢复日志,便于运维确认消费通道已恢复
|
|
747
817
|
- **安全与健壮性加固** — broker 账号/密码按百分号编码、脚本白名单按路径段边界匹配、signals 快照按实例隔离,覆盖相关回归测试
|
|
748
|
-
- **新增测试约定** — 单元测试中对预期触发(如无效 UUID 校验、毒消息丢弃、消费者断连、执行器加载失败等)产生的错误日志必须屏蔽输出,统一使用 `django_simpletask5_example.tests.suppress_expected_errors` 或替换 logger,保证测试输出干净可读;本次同步清理全部此类预期错误日志
|
|
749
818
|
|
|
750
819
|
### 0.2.6
|
|
751
820
|
|
|
752
821
|
- **新增 `canceled` 状态** — 取消已发布但未执行的任务改为独立的终态 `canceled`(原复用 `failed`),Worker 消费到已取消消息时直接跳过;Admin 取消操作、归档/清理/统计(新增 `canceled_count`)、Dashboard 状态图均纳入该状态,取消与执行失败可清晰区分
|
|
753
822
|
- **WorkerSupervisor 守护线程** — 统一的 Worker 线程保活机制,周期性心跳并自动重启异常退出的 Worker 线程,保持目标 Worker 数量,提升长时间运行稳定性
|
|
754
|
-
- **韧性/恢复测试套件** — 新增 `test_resilience_recovery.py`,模拟 Redis、DB、MQ 连接中断后的自动恢复:Worker 启动时 Redis 注册失败重试、消费者断连自动重连、锁引擎中断后消息重入队并在 Redis 恢复后继续处理、DB 瞬断重入队、执行器异常标记 failed/retry、crontab 调度器 Redis 恢复检测等
|
|
755
|
-
- **测试套件稳定性修复** — `test_worker_registry` 隔离全局 `_active_workers` 消除跨用例污染;`test_integration` 动态探测本地可用的 Redis/RabbitMQ 镜像(支持 `DJANGO_SIMPLETASK_REDIS_IMAGE` / `DJANGO_SIMPLETASK_RABBITMQ_IMAGE` 环境变量覆盖),并改为真实 AMQP 握手探测 RabbitMQ 就绪,避免新建容器连接被重置的偶发失败
|
|
756
|
-
- **测试服务脚本** — `start-test-services.sh` 默认使用 6379/5672 端口并动态检测本地镜像,提供 `start` / `stop` / `status` 子命令,便于一键启动测试依赖
|
|
757
823
|
|
|
758
824
|
### 0.2.5
|
|
759
825
|
|
|
@@ -798,7 +864,6 @@ Worker 消费消息 → 获取分布式锁 → 加载执行器 → 执行并保
|
|
|
798
864
|
- **破坏性变更**: 移除 `Task` 模型上的自定义主键,`task_id` 不再是主键字段,改为 `unique=True` 的唯一标识字段。所有 `Task` 子模型将自动获得 Django 默认的自增 `id` 主键。已有数据库需要迁移处理。
|
|
799
865
|
- **统计维度调整**: `TaskExecutionStat` 的统计维度由 `task_model` 改为 `executor_class`,统一覆盖有模型任务和 Cron 定时任务两种场景。对应 API `_update_stats()` 按 `executor_class` 分组聚合。
|
|
800
866
|
- **Admin 优化**: CronJob 列表页移除 `executor_class` 列避免表格撑开;TaskExecutionStat 列表页新增 `executor_class` 列、`created_at` 设为只读修复详情页错误。
|
|
801
|
-
- **测试修复**: 修复 `TransactionTestCase` + `on_commit` 在 SQLite 共享内存连接下因 `TestCase` 残留原子块导致回调不执行的兼容性问题;Redis MQ / RabbitMQ E2E 集成测试全部通过。
|
|
802
867
|
|
|
803
868
|
### 0.1.5
|
|
804
869
|
|