1798c07d42
- 架构和规划文档更新 - 开发、工程、生产发布规则更新 - 服务隔离和升级指南 - README 更新
166 lines
4.6 KiB
Markdown
166 lines
4.6 KiB
Markdown
# 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`
|
|
|
|
**当前代码结构:**
|
|
```rust
|
|
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); // <-- 这里要改
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
**改成:**
|
|
```rust
|
|
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)` |
|
|
|
|
**例:**
|
|
```sql
|
|
-- 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>`
|
|
|
|
**当前:**
|
|
```rust
|
|
impl sqlx::FromRow<'_, sqlx::sqlite::SqliteRow> for Session {
|
|
fn from_row(row: &sqlx::sqlite::SqliteRow) -> Result<Self, sqlx::Error> {
|
|
// column 取值方式对 SQLite 优化
|
|
}
|
|
}
|
|
```
|
|
|
|
**改成:**
|
|
```rust
|
|
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`
|
|
|
|
**改法:**
|
|
```rust
|
|
// 改前
|
|
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 时设置:
|
|
```bash
|
|
export GOOSE_SESSION_DB_URL=postgresql://boot:PASSWORD@120.26.184.105:5432/goose_sessions
|
|
goosed agent
|
|
```
|
|
|
|
数据库需提前创建:
|
|
```bash
|
|
psql -U boot -h 120.26.184.105 -c "CREATE DATABASE goose_sessions;"
|
|
```
|
|
|
|
## 测试步骤
|
|
|
|
1. **编译:**
|
|
```bash
|
|
cd /Users/john/Project/tkmind_go
|
|
GOOSE_SESSION_DB_URL=... cargo build --release --package goose-server --bin goosed
|
|
```
|
|
|
|
2. **启两个实例,挂同一 PG:**
|
|
```bash
|
|
goosed agent &
|
|
goosed agent & # 另一个进程/端口
|
|
```
|
|
|
|
3. **同一 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](goose-scale-architecture-2026-06-26.md) —— 背景与决策
|
|
- [g2 Load Balancing](g2-load-balancing.md) —— 路由与灰度机制
|