PostgreSQL Kit
为有状态 Plugin 的自有 PostgreSQL schema 建立显式生命周期。
lenso-postgres-kit 是一个 Plugin 自有 PostgreSQL schema 的生命周期 kit。它不是
共享 State Plugin、通用 SQL Capability、repository abstraction 或 ORM。数据模型、
query、transaction 边界、备份与保留策略以及专用数据库 role 仍由所属 Plugin 定义。
定义并安装 schema
把 SQL 放在所属 Plugin 的 migrations/ 目录中;Rust 只保留显式版本、名称和文件
顺序:
orders-module/
├── migrations/
│ ├── 001_create_orders.sql
│ └── 002_add_order_status.sql
└── src/lib.rs
CREATE TABLE orders (
id bigint PRIMARY KEY,
total_cents bigint NOT NULL
);
use lenso_postgres_kit::{
Migration, OwnedPostgres, SchemaOperator, SchemaPlan, sql_migrations,
};
const MIGRATIONS: &[Migration] = sql_migrations![
(1, "create-orders", "migrations/001_create_orders.sql"),
(2, "add-order-status", "migrations/002_add_order_status.sql"),
];
async fn install(database_url: &str) -> Result<(), Box<dyn std::error::Error>> {
let plan = SchemaPlan::new("orders_module", MIGRATIONS)?;
SchemaOperator::connect(database_url, plan.clone()).await?.setup().await?;
Ok(())
}
async fn prepare(database_url: &str) -> Result<OwnedPostgres, Box<dyn std::error::Error>> {
let plan = SchemaPlan::new("orders_module", MIGRATIONS)?;
// 运行时 prepare 只验证精确 schema,绝不自动 migration。
Ok(OwnedPostgres::prepare(database_url, plan).await?)
}
路径相对于所属 crate 的 Cargo.toml。sql_migrations! 使用 include_str! 在编译期
嵌入 SQL:文件缺失会直接编译失败,文件变化会触发重编译;运行时不会扫描目录。
版本和名称仍然显式,因此顺序、连续版本校验和 checksum 漂移检测保持确定。
install 必须作为显式安装或部署操作执行,不能放在 Plugin prepare 中。
OwnedPostgres::prepare 成功后,通过 postgres.pool() 使用普通 SQLx query 与
transaction。pool 会把自有 schema 选为 search_path。
显式升级
追加新的 SQL 文件和声明项,不要修改已经应用的文件。新的 Plugin generation 会先返回
PostgresKitError::UpgradeRequired;停止所属 Plugin,执行 operator action,再
prepare 新 generation:
let plan = SchemaPlan::new("orders_module", MIGRATIONS)?;
let outcome = SchemaOperator::connect(database_url, plan)
.await?
.upgrade()
.await?;
println!("{outcome:?}");
Setup 与 upgrade 使用 advisory lock 和一个原子 migration transaction。私有 ledger 记录 version、name 与 checksum。
| 失败 | 含义 |
|---|---|
SetupRequired |
managed schema 尚未安装。 |
UpgradeRequired |
当前链接的 Plugin 有待执行 migration。 |
UnmanagedSchema |
schema 存在但没有 kit ledger;不会自动接管。 |
HistoryDiverged |
已应用的 name、version 或 SQL checksum 被改动。 |
SchemaAhead |
数据库版本比当前 Plugin generation 更新。 |
OwnershipMismatch |
当前数据库 role 不是 schema owner。 |
setup 是幂等的:首次返回 Created,之后返回 AlreadyCurrent。失败时 schema 与
ledger 一起回滚。
在 PostgreSQL 中强制隔离
search_path 只是 query 便利,不是安全边界。每个 Plugin 应使用专用、非 superuser
role,只拥有自己的 schema,并通过数据库 grant 限制权限。共享同一个物理 cluster
不代表可以直接访问其他 Plugin 的 table。
跨 Plugin workflow 使用 Capability call 与应用层协调,不使用共享 SQL transaction。 数据库 URL 通过 Secrets Plugin 解析,绝不写进 Resolved App Plan。
验证 kit
cargo fmt --all -- --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-features
LENSO_POSTGRES_TEST_URL=postgres://... \
cargo test --locked --test postgres_acceptance -- --ignored