fix: meshsafe-cron 定时任务自引入以来从未成功执行 — 三处缺陷
复现而非等日志:容器内直接跑 archive_old_tasks.py 即崩。 1. app/config.py 的 admin_password 是必填字段,cron 环境没有它 → get_settings() 在 import 阶段抛 ValidationError,归档与 VACUUM 全崩。 证据:policy_versions 留 10 行(策略写的留 5 删 5)、scan_tasks_archive 0 行, 而容器 Up 且 RestartCount=0,没有任何存活信号暴露。给占位 env,不把真实口令复制进 cron。 2. VACUUM 不能在事务块里跑(ActiveSqlTransaction),原代码用了 engine.begin(), 改用 AUTOCOMMIT 连接;实测 6 张表 0.25s 完成。 3. ValidationError 把整个 settings dict 打进 stderr,被 run() 原样写进 docker 日志, 含 DB 连接串与 master key 尾部 → 加 _redact() 落盘前抹掉,验证输出中不可见。 cron 不监听 8443,镜像层 HEALTHCHECK 恒为 unhealthy,调度器真挂掉时该信号失效,故关闭。
This commit is contained in:
@@ -25,6 +25,20 @@ logging.basicConfig(
|
||||
log = logging.getLogger("scheduler")
|
||||
|
||||
|
||||
_SECRET_ENV_KEYS = ("MESHSAFE_DB_URL", "MESHSAFE_MASTER_KEY", "MESHSAFE_ADMIN_PASSWORD",
|
||||
"MESHSAFE_API_KEY", "MESHSAFE_API_CLIENTS")
|
||||
|
||||
|
||||
def _redact(text: str) -> str:
|
||||
"""崩溃日志里 pydantic 会把整个 settings dict(含 DB 口令)原样打出来,落盘前抹掉。"""
|
||||
out = text
|
||||
for key in _SECRET_ENV_KEYS:
|
||||
val = os.environ.get(key, "")
|
||||
if val and len(val) >= 8:
|
||||
out = out.replace(val, "***")
|
||||
return out
|
||||
|
||||
|
||||
def run(cmd: list[str], label: str) -> None:
|
||||
"""跑一个命令,捕获输出并记日志。"""
|
||||
log.info(f"[{label}] 开始执行: {' '.join(cmd)}")
|
||||
@@ -32,10 +46,10 @@ def run(cmd: list[str], label: str) -> None:
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)
|
||||
if proc.stdout:
|
||||
for line in proc.stdout.strip().splitlines():
|
||||
log.info(f"[{label}] {line}")
|
||||
log.info(f"[{label}] {_redact(line)}")
|
||||
if proc.stderr:
|
||||
for line in proc.stderr.strip().splitlines():
|
||||
log.warning(f"[{label}] {line}")
|
||||
log.warning(f"[{label}] {_redact(line)}")
|
||||
if proc.returncode == 0:
|
||||
log.info(f"[{label}] ✅ 完成")
|
||||
else:
|
||||
@@ -47,7 +61,11 @@ def run(cmd: list[str], label: str) -> None:
|
||||
|
||||
|
||||
def run_vacuum() -> None:
|
||||
"""VACUUM ANALYZE — 维护表统计信息。"""
|
||||
"""VACUUM ANALYZE — 维护表统计信息。
|
||||
|
||||
VACUUM 不能在事务块里跑(psycopg2 会抛 ActiveSqlTransaction),
|
||||
所以用 AUTOCOMMIT 连接而不是 engine.begin()。
|
||||
"""
|
||||
try:
|
||||
from app.db import engine
|
||||
from sqlalchemy import text
|
||||
@@ -57,13 +75,14 @@ def run_vacuum() -> None:
|
||||
"scan_findings", "idempotency_records",
|
||||
"policy_versions", "audit_logs",
|
||||
]
|
||||
with engine.begin() as conn:
|
||||
with engine.connect() as raw:
|
||||
conn = raw.execution_options(isolation_level="AUTOCOMMIT")
|
||||
for tbl in tables:
|
||||
log.info(f"[VACUUM] {tbl} ...")
|
||||
conn.execute(text(f"VACUUM ANALYZE {tbl}"))
|
||||
log.info("[VACUUM] ✅ 全部完成")
|
||||
except Exception as e:
|
||||
log.error(f"[VACUUM] ❌ 异常: {e}")
|
||||
log.error(f"[VACUUM] ❌ 异常: {_redact(str(e))}")
|
||||
|
||||
|
||||
def seconds_until(target_hour: int, target_minute: int, weekday: int | None = None) -> float:
|
||||
|
||||
Reference in New Issue
Block a user