Lack of isolation:Celery beat 与普通业务共享同一进程组,修复后仍担心会影响业务请求的 SLA。
Migrations not idempotent:Liquibase 迁移脚本非幂等。上线后出现索引已存在报错,引发二次部署卡顿。
. 现象与初步判断
现象
Container 内存使用一直增加,没有回落趋势。
P普通业务 worker 和 beat worker 都在同一容器内运行。
Docker stats 可以观察到内存稳步增长。
Business 流量并不大,按理说不应该消耗这么多内存。
初步判断
Due to low traffic yet steady memory growth,suspected:
Memory leak:A code path keeps references.
Large result set loading:A query pulls massive rows into memory.
Timed‑task accumulation:Celery beat tasks may allocate memory each run.
A 's RSS as an outlier.
确认进程身份
memray attach
说到Error。
Cannot find a supported lldb or gdb executable and sys.remote_exec is not available.
The container lacked gdb/lldb;installing gdb resolved this.第二次尝试:加 --live memray attach --live -o trace.bin Error:memray: error: unrecognized arguments: --live
`--live` belongs to `memray run`,not `attach`.第三次尝试:仅 -o 不指定时长 `memray attach -o trace.bin ` entered TUI but produced an empty file because no duration was set and detach never occurred.最终正确命令 memray attach -o /tmp/trace.bin -f --duration 300 --follow-fork
`-o /tmp/trace.bin` – output file
`-f` – force overwrite
`--duration 300` – sample for 5 minutes
`--follow-fork` – trace child processes created by Celery forks
The command automatically detaches after specified duration and writes a complete `trace.bin`.. memray 关键证据 memray stats /tmp/trace.bin | head -
/ pre
Key excerpt :
Total memory allocated: 0.444GB
Peak memory usage: 0.735GB
Top allocations by function:
do_execute 0.163GB ← SQLAlchemy executor
_populate_full …说起来,← ORM row population
fetchall …/ pre
This confirms that heartbeat worker’s memory explosion originates from a massive DB query that materializes thousands of ORM objects in one go.
. 定位到 Celery 心跳任务
bash
grep -rn “task_status_sync_job” --include=”*.py”
text
app/scheduler/task_status_sync_job.py
The task is scheduled by Celery beat with `soft_time_limit=` seconds.
### 深入心跳同步逻辑
Core logic lives in `domain/ability/task_status_sync_ability.py`,which performs two queries:
1️⃣ `get_pending_and_running_tasks` – fetches all **PENDING** and **RUNNING** tasks.
2️⃣ `get_success_tasks_with_incomplete_progress` – fetches all **SUCCESS** tasks whose `progress!= ,`.
Both results are n synchronized back to Celery.
### 发现问题
In `infrastructure/gatewayimpl/task_gateway_impl.py` two methods **reuse heavy business‑side query APIs**:
* SELECT * .
* `.all` loads every row at once.
* No time window → full‑table scan that grows over time.
* Filtering happens in Python after loading all rows.