代码修改

This commit is contained in:
吉鑫升 committed 2026-05-21 20:19:44 +08:00
1 parent 843eda6fea
commit ac4424bab2
3 files changed
+48 -10

No files matched your search

+1 -1
View File
@@ -154,7 +154,7 @@ memory:
hindsight: # provider=hindsight 时生效,需另行部署 Hindsight 服务 hindsight: # provider=hindsight 时生效,需另行部署 Hindsight 服务
mode: local_external # 'cloud' | 'local_embedded' | 'local_external' mode: local_external # 'cloud' | 'local_embedded' | 'local_external'
api_url: http://localhost:8765 api_url: http://47.88.25.99:18888
api_key: "" # 部署 Hindsight 后填,或改成 $HINDSIGHT_API_KEY 并设同名环境变量 api_key: "" # 部署 Hindsight 后填,或改成 $HINDSIGHT_API_KEY 并设同名环境变量
# 注意:写成 $VAR 但环境变量未设置会导致配置加载失败 # 注意:写成 $VAR 但环境变量未设置会导致配置加载失败
bank_id_template: "deerflow-user-{user_id}" bank_id_template: "deerflow-user-{user_id}"
@@ -13,6 +13,7 @@ from __future__ import annotations
import asyncio import asyncio
import json import json
import logging import logging
import os
import sys import sys
from importlib.util import find_spec from importlib.util import find_spec
from pathlib import Path from pathlib import Path
@@ -77,6 +78,10 @@ async def _run_alembic_upgrade(url: str) -> None:
operable on the ``create_all`` schema; the operator just won't get operable on the ``create_all`` schema; the operator just won't get
in-place fixes until they investigate. in-place fixes until they investigate.
""" """
if os.getenv("DEER_FLOW_SKIP_ALEMBIC", "").lower() in ("1", "true", "yes"):
logger.warning("DEER_FLOW_SKIP_ALEMBIC is set; skipping Alembic migrations (schema stays at create_all)")
return
ini_path = Path(__file__).parent / "migrations" / "alembic.ini" ini_path = Path(__file__).parent / "migrations" / "alembic.ini"
if not ini_path.exists(): if not ini_path.exists():
logger.warning("Alembic config not found at %s; skipping auto-migrate", ini_path) logger.warning("Alembic config not found at %s; skipping auto-migrate", ini_path)
@@ -102,9 +107,26 @@ async def _run_alembic_upgrade(url: str) -> None:
# at the directory of alembic.ini itself — already correct for us. # at the directory of alembic.ini itself — already correct for us.
command.upgrade(cfg, "head") command.upgrade(cfg, "head")
# Bound the upgrade so a stuck DDL (e.g. MySQL metadata-lock wait) cannot
# hang startup forever. Tunable via DEER_FLOW_ALEMBIC_TIMEOUT (seconds).
# NOTE: asyncio.wait_for cancels the *await* on timeout; the worker thread
# itself keeps running (Python can't kill threads). That's acceptable —
# the orphan thread will exit when its MySQL connection eventually errors
# or when the process restarts. The point here is to unblock startup.
try: try:
await asyncio.to_thread(_do_upgrade) timeout_s = float(os.getenv("DEER_FLOW_ALEMBIC_TIMEOUT", "60"))
except ValueError:
timeout_s = 60.0
try:
await asyncio.wait_for(asyncio.to_thread(_do_upgrade), timeout=timeout_s)
logger.info("Alembic migrations applied (or already up-to-date)") logger.info("Alembic migrations applied (or already up-to-date)")
except asyncio.TimeoutError:
logger.error(
"Alembic upgrade did not finish within %.0fs; continuing startup on create_all schema. "
"Likely cause: MySQL metadata-lock wait — check SHOW FULL PROCESSLIST for a blocking transaction.",
timeout_s,
)
except Exception: except Exception:
logger.exception("Auto-applying Alembic migrations failed; continuing with create_all schema") logger.exception("Auto-applying Alembic migrations failed; continuing with create_all schema")
@@ -257,7 +279,7 @@ async def init_engine(
# Apply in-place schema fixes (column widenings, etc.) that ``create_all`` # Apply in-place schema fixes (column widenings, etc.) that ``create_all``
# cannot do on its own. Failures don't abort startup — see the helper. # cannot do on its own. Failures don't abort startup — see the helper.
await _run_alembic_upgrade(url) # await _run_alembic_upgrade(url)
logger.info("Persistence engine initialized: backend=%s", backend) logger.info("Persistence engine initialized: backend=%s", backend)
@@ -9,9 +9,11 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
import os
from logging.config import fileConfig from logging.config import fileConfig
from alembic import context from alembic import context
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy.ext.asyncio import create_async_engine
from deerflow.persistence.base import Base from deerflow.persistence.base import Base
@@ -44,13 +46,27 @@ def run_migrations_offline() -> None:
context.run_migrations() context.run_migrations()
def do_run_migrations(connection): def do_run_migrations(connection):
render_as_batch = connection.dialect.name == "sqlite" render_as_batch = connection.dialect.name == "sqlite"
context.configure(
connection=connection, # On MySQL, shrink the session lock waits so a blocked DDL fails fast
target_metadata=target_metadata, # instead of hanging on the server default (lock_wait_timeout = 31536000s
render_as_batch=render_as_batch, # Required for SQLite ALTER TABLE support # = 1 year; innodb_lock_wait_timeout = 50s). Without this, an idle
) # transaction on another connection can freeze startup indefinitely.
# Tunable via DEER_FLOW_ALEMBIC_LOCK_WAIT (seconds, default 15).
if connection.dialect.name == "mysql":
try:
lock_wait = int(os.getenv("DEER_FLOW_ALEMBIC_LOCK_WAIT", "15"))
except ValueError:
lock_wait = 15
connection.execute(text(f"SET SESSION lock_wait_timeout = {lock_wait}"))
connection.execute(text(f"SET SESSION innodb_lock_wait_timeout = {lock_wait}"))
context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=render_as_batch, # Required for SQLite ALTER TABLE support
)
with context.begin_transaction(): with context.begin_transaction():
context.run_migrations() context.run_migrations()