1798c07d42
- 架构和规划文档更新 - 开发、工程、生产发布规则更新 - 服务隔离和升级指南 - README 更新
4.6 KiB
4.6 KiB
Goose SessionStorage SQLite → PostgreSQL 迁移指南
目标:让 goosed 多实例真正 stateless,session 状态存 Postgres 而非本地 SQLite。
工作量:改 1 个结构体 + 3 处 SQL,新增 connection pool 初始化。预计 2-4 小时。
前置:fork
/Users/john/Project/tkmind_go或在那个仓库新建分支。
改造点
1. crates/goose/src/session/session_manager.rs:525
当前代码结构:
pub struct SessionStorage {
pool: SqlitePool, // <-- 改这里
// ...
}
impl SessionStorage {
pub fn new(data_dir: PathBuf) -> Self {
let options = SqliteConnectOptions::default()
.filename(data_dir.join(SESSIONS_FOLDER))
.create_if_missing(true)
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal);
let pool = SqlitePoolOptions::new()
.connect_lazy_with(options); // <-- 这里要改
// ...
}
}
改成:
pub struct SessionStorage {
pool: sqlx::postgres::PgPool, // 改这行
// ...
}
impl SessionStorage {
pub fn new(connection_string: String) -> Self {
// 从环境变量或参数读取 PostgreSQL 连接串
// e.g. GOOSE_SESSION_DB_URL=postgresql://...
let pool = sqlx::postgres::PgPoolOptions::new()
.max_connections(20)
.connect_lazy(&connection_string);
// ...
}
}
2. schema.sql / 迁移 SQL
SQLite 专用语法转 PostgreSQL:
| 问题 | SQLite | PostgreSQL | 改法 |
|---|---|---|---|
| 主键自增 | INTEGER PRIMARY KEY AUTOINCREMENT |
SERIAL PRIMARY KEY |
改为 BIGSERIAL 或 GENERATED ALWAYS AS IDENTITY |
| 类型 | TEXT(什么都行) |
严格类型(JSONB/TEXT/TIMESTAMP) | 用 JSONB 存 JSON,用 TIMESTAMP 存时间戳 |
| 模式检查 | sqlite_master |
information_schema |
改用 information_schema.tables |
| WAL/Journal | PRAGMA journal_mode=WAL |
N/A | 删除,PG 有自己的 WAL |
| Collate | COLLATE NOCASE |
用 collation 或 LOWER() | 改成 LOWER(column) = LOWER($1) |
例:
-- SQLite
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
data JSON,
created_at INTEGER
);
-- PostgreSQL
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
data JSONB,
created_at BIGINT
);
3. FromRow<SqliteRow> → FromRow<PgRow>
当前:
impl sqlx::FromRow<'_, sqlx::sqlite::SqliteRow> for Session {
fn from_row(row: &sqlx::sqlite::SqliteRow) -> Result<Self, sqlx::Error> {
// column 取值方式对 SQLite 优化
}
}
改成:
impl sqlx::FromRow<'_, sqlx::postgres::PgRow> for Session {
fn from_row(row: &sqlx::postgres::PgRow) -> Result<Self, sqlx::Error> {
// 取值方式对 PG 优化(JSONB 用 get_raw 后 JSON 解析)
}
}
4. 调用处:初始化时读环境变量
地点: main.rs / 启动 SessionManager
改法:
// 改前
let session_manager = SessionManager::new(data_dir);
// 改后
let session_db_url = std::env::var("GOOSE_SESSION_DB_URL")
.expect("set GOOSE_SESSION_DB_URL env");
let session_manager = SessionManager::new(session_db_url);
环境变量
启动 goosed 时设置:
export GOOSE_SESSION_DB_URL=postgresql://boot:PASSWORD@120.26.184.105:5432/goose_sessions
goosed agent
数据库需提前创建:
psql -U boot -h 120.26.184.105 -c "CREATE DATABASE goose_sessions;"
测试步骤
-
编译:
cd /Users/john/Project/tkmind_go GOOSE_SESSION_DB_URL=... cargo build --release --package goose-server --bin goosed -
启两个实例,挂同一 PG:
goosed agent & goosed agent & # 另一个进程/端口 -
同一 session 轮换到两个实例:
- 客户端起 session on instance 1
- 后续命令路由到 instance 2 —— 状态应无缝恢复
文件清单
修改的文件(全在 crates/goose/src/):
session/session_manager.rs—— 3-5 处改动session/mod.rs—— 可能有 FromRow(如果有多个 impl)session/legacy.rs—— 如果涉及导入(通常不用改)Cargo.toml—— 检查 sqlx features(postgres要打开)
风险与回滚
- 数据迁移: SQLite→PG 用
pgloader命令行工具(一条命令搞定 schema+数据+类型映射) - 灰度: 先让新 goosed 实例用 PG,老实例仍用 SQLite,流量按 session 分配。验证无误后全量切换。
- 回滚: PG 有完整 session 数据,可随时起新 SQLite 实例从 PG 导回。
相关文档
- Goose Scale Architecture —— 背景与决策
- g2 Load Balancing —— 路由与灰度机制