feat(rfc-001): 回应小墨 review — runtime 注入 db/peers、migration rollback、drizzle config、schema 新鲜度
This commit is contained in:
@@ -113,22 +113,50 @@ compute 拿到的 db 实例是 Drizzle 包装过的,查询全部 type-safe:
|
||||
|
||||
```typescript
|
||||
// senses/cpu-usage/index.ts
|
||||
import { db } from './schema'
|
||||
import { samples } from './schema'
|
||||
|
||||
export async function compute(): Promise<number | null> {
|
||||
// db 和 peers 由 engine runtime 注入,不需要用户创建
|
||||
export async function compute(
|
||||
db: DrizzleDB,
|
||||
peers: Readonly<Record<string, DrizzleDB>>
|
||||
): Promise<number | null> {
|
||||
const load = os.loadavg()[0]
|
||||
await db.insert(samples).values({ ts: Date.now(), value: load })
|
||||
return load
|
||||
}
|
||||
```
|
||||
|
||||
**Cross-sense 类型安全读取:** compute 通过 `peers` 参数拿到其他 Sense 的只读 db 实例。类型来自 import 对方的 schema:
|
||||
|
||||
```typescript
|
||||
// senses/active-tasks/index.ts
|
||||
import { tasks } from './schema'
|
||||
import { samples } from '../cpu-usage/schema' // 只导入类型定义
|
||||
|
||||
export async function compute(
|
||||
db: DrizzleDB,
|
||||
peers: Readonly<Record<string, DrizzleDB>>
|
||||
): Promise<TaskSummary | null> {
|
||||
// 自己的 db:读写
|
||||
const activeTasks = await db.select().from(tasks).where(...)
|
||||
// peer 的 db:只读,类型安全
|
||||
const cpuLoad = await peers['cpu-usage'].select().from(samples).orderBy(desc(samples.ts)).limit(1)
|
||||
return { activeTasks, cpuLoad }
|
||||
}
|
||||
```
|
||||
|
||||
**为什么用 Drizzle 而不是手写 SQL migration:**
|
||||
|
||||
Nerve 的 Sense 开发者是机器上的 Coding Agent(通过 Nerve 自身的 Workflow 自举开发)。Agent 行为的正确性应靠确定性工具保证,不应依赖概率模型的"自律"。Drizzle 让 agent 只写一个东西(`schema.ts`),migration 和类型都是机械派生,消除了 TS 与 SQL 不一致的风险。
|
||||
|
||||
**引擎职责:** 运行时只执行 migration SQL(`drizzle migrate`),不依赖 drizzle-kit。生成 migration 是开发时(workflow role action)的事。
|
||||
|
||||
**Migration rollback:** Drizzle 不支持 down migration,但 Sense 的 SQLite 是派生数据——坏了删 `.db` 文件,engine 重启时自动重跑 migration 重建。不需要 rollback 机制。
|
||||
|
||||
**drizzle.config:** 不需要每个 Sense 各一份。Engine runtime 统一参数化——根据 sense name 确定 `.db` 路径和 `migrations/` 目录,调用 `drizzle-kit generate` 时动态传入。Sense 开发者只写 `schema.ts`。
|
||||
|
||||
**Schema 新鲜度:** Engine 启动时不校验 `schema.ts` 与 `migrations/` 是否同步。这是开发时的职责——创建/修改 Sense 的 Workflow 负责跑 `drizzle-kit generate` 并提交 migration。运行时只管执行已有的 migration SQL。
|
||||
|
||||
#### Sense 不知道 Workflow
|
||||
|
||||
Sense 只感知世界并产出 Signal,永远不关心"谁在听"或"听了之后要做什么"。Sense 不引用 Workflow、不返回 `ThreadStart`、不知道自己的 Signal 会触发什么动作。
|
||||
@@ -307,6 +335,30 @@ systemd / pm2
|
||||
|
||||
worker_thread 的隔离是假的——用户代码的 `process.exit()`、native module segfault、OOM 都会杀死主进程。只有进程边界才是真正的隔离墙。
|
||||
|
||||
#### Worker 架构:Engine Runtime + 用户代码
|
||||
|
||||
Worker 不是用户代码直接跑的进程,而是 **engine 提供的 runtime 加载用户代码**。用户代码是被 import 的模块,不是入口。
|
||||
|
||||
```
|
||||
nerve-engine (kernel)
|
||||
└─ nerve worker sense --group system
|
||||
├─ runtime bootstrap(engine 代码)
|
||||
│ ├─ 建立 IPC
|
||||
│ ├─ 读 nerve.yaml,找到 group 里有哪些 sense
|
||||
│ ├─ 对每个 sense:打开 .db → 跑 migration → drizzle 包装
|
||||
│ ├─ 构建 peers 只读连接
|
||||
│ └─ 发 { type: 'ready' }
|
||||
│
|
||||
└─ 用户代码(被 import 进来)
|
||||
├─ cpu-usage/schema.ts ← 纯类型定义
|
||||
└─ cpu-usage/index.ts ← compute 函数,拿到注入的 db
|
||||
```
|
||||
|
||||
这意味着:
|
||||
- **用户代码不操心基础设施**——IPC、db 初始化、migration 都是 runtime 的事
|
||||
- **compute 函数签名简单**——engine 注入 `db`(自己的)和 `peers`(只读),用户只写业务逻辑
|
||||
- **隔离天然成立**——用户代码跑在 engine 控制的沙箱里,crash 只影响同 group
|
||||
|
||||
#### Sense Worker
|
||||
|
||||
Sense 按 group 分组,同 group 共享一个 worker 进程。用户决定隔离粒度。
|
||||
|
||||
Reference in New Issue
Block a user