oclaw/docs/ASSISTANT_PG_MIGRATION.md
2026-05-14 10:24:23 +08:00

10 KiB
Raw Blame History

Assistant 库:SQLite → PostgreSQL 迁移指南

本文说明如何将 助手主持久化(会话、chat_message、设置、租户等)从默认 SQLite 迁到 PostgreSQL,以及割接前后的操作顺序与排障要点。实现上仍通过同一套 SqliteStore API;仅底层连接与 SQL 方言不同。

更完整的环境变量说明见 ENVIRONMENT_VARIABLES.md 中的「存储与迁移」一节;本文侧重 操作步骤与脚本。


1. 何时需要读本文

  • 生产或预发要把 assistant 主库 从 data/ai_ops.sqlite(或自定义 SQLite 路径)迁到 PostgreSQL。
  • 需要 一次性导入历史数据,再切换 AIA_ASSISTANT_DB_BACKEND=postgresql 上线。
  • 排查「PG 上表空 / 导入失败 / 网关连错库」等问题。

若只做 新部署、无历史 SQLite 数据:在空库上建 schema 后,直接配置 PG 与 postgresql 后端即可,不必跑数据导入脚本。


2. 核心概念

项目 说明
后端选择 AIA_ASSISTANT_DB_BACKEND:sqlite(默认)或 postgresql(别名 pg / postgres 等)。
PG 连接串 AIA_ASSISTANT_DATABASE_URL(或 OPS_ASSISTANT_DATABASE_URL / AIA_ASSISTANT_PG_DSN 等别名)。SQLAlchemy 使用时会规范为 postgresql+psycopg://…。
Schema 来源 推荐 alembic upgrade head(alembic.ini 的 script_location = assistant_migrations)。等价方式:在目标库执行 svc/persistence/ddl/postgresql_bootstrap.sql(与迁移首版语义一致,二选一即可,勿混用导致重复建表报错)。
数据导入 runtime/operations/scripts/migrate_assistant_sqlite_to_postgresql.py:只拷数据,不建表;要求目标 PG 已存在 schema。默认要求 PG 中待拷贝表 为空(可用 --allow-non-empty 跳过检查,自担重复/FK 风险)。
网关 进程内通过 get_assistant_store() 单例连库;切换 URL 或后端后需重启网关(必要时 reset 相关进程环境)。

3. 推荐总体顺序( checklist )

  1. 在 PostgreSQL 上创建数据库(例如 oclaw),为应用用户授予 CREATE / USAGE on schema public 及后续表所需权限。
  2. 在目标库上完成 schema:alembic upgrade head(仓库根执行,且已配置指向该库的 DATABASE_URL / 环境变量),或执行 postgresql_bootstrap.sql。
  3. 不要在未建表的空库上直接启动依赖全表的生产网关(会报错或产生不一致状态)。应完成第 2 步后再启动,或先保持 sqlite 仅跑导入机。
  4. 使用下文 割接脚本 或 直接调用 migrator:先做 --dry-run,确认行数与表列表无误后再实导。
  5. 将运行环境改为 AIA_ASSISTANT_DB_BACKEND=postgresql 并设置 AIA_ASSISTANT_DATABASE_URL(与导入时同一目标库)。
  6. 重启网关及所有使用 get_assistant_store() 的 worker,使新配置生效。
  7. 验证管理台会话列表、发一条测试消息、必要时跑 AIA_TEST_PG_URL 相关测试(见下文)。

4. Schema:Alembic(推荐)

仓库根:

# 已设置 AIA_ASSISTANT_DATABASE_URL 指向目标 PG,且已安装依赖(含 alembic、psycopg)
cd /path/to/oclaw
export PYTHONPATH=.
alembic upgrade head

首版迁移 assistant_migrations/versions/001_assistant_pg_initial.py 会读取 svc/persistence/ddl/postgresql_bootstrap.sql 并逐条执行(仅当方言为 postgresql 时)。

说明:downgrade() 未实现;回滚依赖 数据库备份 / 文件备份,而非 Alembic 反向迁移。


5. 数据导入:migrate_assistant_sqlite_to_postgresql.py

路径:runtime/operations/scripts/migrate_assistant_sqlite_to_postgresql.py。

行为摘要

  • 从 SQLite 读表顺序(按 PRAGMA foreign_key_list 拓扑),仅插入 SQLite 与 PG public 中同名的共有列。
  • 默认若任一待拷贝表在 PG 中 已有行 则 退出(避免重复);--allow-non-empty 可关闭该检查。
  • --dry-run:只统计源表行数,不写 PG。

常用参数

参数 含义
--sqlite PATH 源 SQLite 文件路径。
--sqlite-from-db-path 使用 db_path()(受 AIA_ASSISTANT_DB_PATH 等影响)作为源库。
--load-system-env 合并 _local/system.env(与网关 load_system_env 思路一致,便于设备上只维护一份 env)。
--pg-url URL 目标 PG;不设则从环境变量读取(见脚本 --help)。
--dry-run 预演。
--allow-non-empty 允许目标表非空(慎用)。
--batch N 批量插入行数,默认 500。

示例(本机预演)

cd /path/to/oclaw
export PYTHONPATH=.
export AIA_ASSISTANT_DATABASE_URL='postgresql+psycopg://USER:PASS@127.0.0.1:5432/oclaw'
python runtime/operations/scripts/migrate_assistant_sqlite_to_postgresql.py \
  --load-system-env --sqlite-from-db-path --dry-run
# 确认无报错后去掉 --dry-run 再执行

显式路径示例

python runtime/operations/scripts/migrate_assistant_sqlite_to_postgresql.py \
  --sqlite data/ai_ops.sqlite \
  --pg-url 'postgresql+psycopg://postgres:pass@127.0.0.1:5432/oclaw'

6. 割接包装脚本(备份 + dry-run + 导入)

在跑正式导入前,强烈建议备份 SQLite 文件。脚本会把副本放到 data/pg_cutover_backups/(可改参数或环境变量)。

6.1 Windows(PowerShell)

runtime/operations/scripts/cutover_sqlite_to_postgresql.ps1

典型用法:

cd D:\path\to\oclaw
# 若 URL 写在 _local\system.env 中:
.\runtime\operations\scripts\cutover_sqlite_to_postgresql.ps1 -LoadSystemEnv
# 或显式传入:
.\runtime\operations\scripts\cutover_sqlite_to_postgresql.ps1 -PgUrl "postgresql+psycopg://..."

常用开关:-SqlitePath、-BackupDir、-SkipDryRun、-DryRunOnly、-NoBackup(不推荐生产)、-LoadSystemEnv。

6.2 Linux / bash

runtime/operations/scripts/cutover_sqlite_to_postgresql.sh

  • 环境变量:SQLITE_PATH、BACKUP_DIR、NO_BACKUP、SKIP_DRY_RUN、DRY_RUN_ONLY 等(见脚本头部注释)。
  • 默认会 --load-system-env 并解析 SQLite 路径。

仅 导入(不复制备份逻辑时也可用轻量包装):

runtime/operations/scripts/assistant_import_sqlite_to_postgresql.sh(内部调用 migrator 的 --load-system-env --sqlite-from-db-path)。


7. 切换运行时与网关

导入完成后:

  1. 设置 AIA_ASSISTANT_DB_BACKEND=postgresql。
  2. 设置 AIA_ASSISTANT_DATABASE_URL(与导入目标一致)。
  3. 重启 网关、wiki worker、渠道 worker 等所有持有 DB 连接的进程。

注意:get_assistant_store() 在进程内会按 (backend, 连接键) 缓存;连接键对 PostgreSQL 使用 URL 的哈希(避免在内存键中携带明文口令)。改环境后必须重启进程,否则会连旧库。


8. 验证与自动化测试

  • 管理台:会话列表、打开历史会话、发送一条新消息,确认读写正常。
  • 单测:设置环境变量 AIA_TEST_PG_URL 后,部分用例会对真实 PG 跑冒烟(例如 tests/test_database_backend.py)。详见 ENVIRONMENT_VARIABLES.md 中 AIA_TEST_PG_URL 说明。

9. 备份与回滚

  • 割接脚本 默认将 SQLite 复制到 data/pg_cutover_backups/<原名>_pre_pg_<时间戳>.sqlite。
  • 若导入后需回退到 SQLite:恢复该备份文件为 data/ai_ops.sqlite(或你的 AIA_ASSISTANT_DB_PATH),将 AIA_ASSISTANT_DB_BACKEND 改回 sqlite 或未设置,重启服务。
  • PostgreSQL 侧回滚:依赖实例级备份(快照 / pg_dump),不在应用脚本内自动完成。

10. 仅清空 PG 中的对话数据(可选)

若要在 PostgreSQL 上删除所有会话及相关行(危险操作),使用:

  • runtime/operations/scripts/clear_postgres_chat_sessions.ps1(加载 _local/system.env、强制 PG、设置确认变量后调用 Python),或
  • python runtime/operations/scripts/clear_all_chat_sessions.py --yes --postgresql
    且必须同时设置 AIA_CONFIRM_CHAT_SESSION_WIPE=1。

详见脚本内说明;勿在未确认库名时针对生产执行。


11. 常见问题(排障)

现象 可能原因 处理
导入报「表非空」 PG 已有数据 换新库或 --allow-non-empty(谨慎)
网关仍像连 SQLite 环境未生效 / 未重启 检查进程环境、assistant_store 单例
invalid_credentials / 连接失败 URL 或网络或权限 用 psql 或 psycopg 单独测连
部分表未拷贝 PG 无同名表 先 alembic upgrade head;查看 migrator 打印的 skip (not in PG public schema)
仍为 SQLite 但已填 PG URL 后端未切到 postgresql 首次解析 URL 会 UserWarning;确认 AIA_ASSISTANT_DB_BACKEND 与重启
连接 PG 长时间无响应 网络/防火墙 可调 AIA_ASSISTANT_PG_CONNECT_TIMEOUT(默认 10s,见 ENVIRONMENT_VARIABLES.md)

12. 相关文件索引

路径 用途
alembic.ini Alembic 配置,script_location = assistant_migrations
assistant_migrations/ PG schema 版本链
svc/persistence/ddl/postgresql_bootstrap.sql 初始 DDL(与首版迁移同源)
svc/persistence/pg_adapter.py psycopg 连接(含 connect_timeout)
svc/persistence/assistant_store.py get_assistant_store() 工厂
runtime/operations/scripts/migrate_assistant_sqlite_to_postgresql.py 数据导入
runtime/operations/scripts/cutover_sqlite_to_postgresql.ps1 / .sh 备份 + 预演 + 导入
runtime/operations/scripts/clear_all_chat_sessions.py 清空会话(需确认 env)
docs/ENVIRONMENT_VARIABLES.md 环境变量权威列表

维护建议:若变更割接流程或新增迁移步骤,请同步更新本文与 ENVIRONMENT_VARIABLES.md 中的摘要,避免文档分叉。