trueforge Postgres 持久层开发规范:timestamptz、防 N+1 与 lock_timeout 迁移的工程实践
【免费下载链接】trueforgeThe open-source agent harness - the runtime layer that turns an LLM into a working agent.项目地址https://gitcode.com/gh_mirrors/tr/trueforge点击查看免费下载本篇指南围绕 trueforge 开源仓库中 Postgres 持久层开发约定即packages/trueforge/src/db/postgres/AGENTS.md展开逐条拆解 trueforge 在时序列类型、UTC 时间戳序列化、批量查询、迁移锁超时与自定义 schema 上的硬性规则。读完你不仅能理解每条规则背后的数据库原理还能直接在仓库源码中找到对应实现并将其复用到自己的 Postgres 工程里——从写出一条合规的 Kysely 迁移到用pnpm migrate在非publicschema 中完成建表。trueforge 是一个把 LLM 变成可用 Agent的开源 agent harness其持久层同时支持 SQLitestandalone 模式与 Postgres生产模式并统一通过 Kysely 查询构建器访问数据库。Postgres 路径的全部代码集中在 packages/trueforge/src/db/postgres 目录内含连接客户端client.ts、迁移 CLImigrate-cli.ts、schema 引导逻辑schema.ts以及按业务域划分的多个 storeagent-store/、session-store/、schedule-store/等。该目录下的 AGENTS.md 是一份面向开发者的五条硬性编码约定本文即以其为骨架逐条结合源码展开。一、时序列必须使用timestamptztimestamp with time zone约定原文Temporal columns MUST usetimestamptz(timestamp with time zone). Do not usetimestampwithout time zone.Postgres 中的timestamp不带时区与timestamptz带时区行为差异巨大timestamp只存储墙上时间不含时区语义同一数值在不同timezone会话参数下会被解释成不同时刻跨地域部署或客户端时区不一致时极易产生错乱timestamptz底层以 UTC 存储展示时再按会话timezone转换语义唯一、可比较、可排序是分布式服务处理时间的唯一正确选择。该规则在 trueforge 中不是一句口号而是逐条落实在每个迁移文件里。以最核心的 session 存储迁移 为例session、turn、turn_thread、session_event、thread_context_log、thread_capability_state六张表的所有时间列created_at、updated_at一律声明为timestamptzawait db.schema .createTable(session) ... .addColumn(created_at, timestamptz, col col.notNull()) .addColumn(updated_at, timestamptz, col col.notNull())同样地agent 注册表迁移 中的agent.created_at/agent.updated_at、以及 init 迁移 之后的所有增量迁移均严格遵循timestamptz。在 trueforge 的仓库中搜索timestamptz可以确认没有任何一张业务表使用不带时区的timestamp。这条约定同时服务于下面第二条规则——只有列类型是timestamptz应用层以 UTC 瞬时写入的 ISO 字符串才能被无歧义地解析。二、应用时间戳必须视为 UTC 瞬时用toISOString()序列化约定原文Application timestamps MUST be treated as UTC instants. Serialize withDate.prototype.toISOString()(always...Zwith milliseconds).Date.prototype.toISOString()始终输出形如2026-10-09T06:47:53.123Z的字符串——带Z后缀、精确到毫秒、无时区歧义。trueforge 要求所有落在timestamptz列上的值都以这种格式序列化使得应用层写入格式与数据库存储语义保持同一套时间坐标系UTC 瞬时。该规则在 store 层的映射代码中反复出现。以 PostgresAgentStore.ts 为例读取行后向领域模型转换时直接调用toISOString()created_at: row.created_at.toISOString(), updated_at: row.updated_at.toISOString(),同样的模式还出现在 PostgresMcpServerStore.ts、PostgresModelProviderStore.ts 与 PostgresSandboxEnvironmentStore.ts 中。背后原理是node-postgres 驱动在解析timestamptz列时得到的是 JavaScriptDate对象而Date本质上就是 UTC 瞬时epoch millis向外序列化时若用toLocaleString()、toString()或手工拼接会引入本地时区偏移导致同一个值在不同机器上写出不同的字符串。toISOString()则保证任何环境下输出一致且与第一条约定的timestamptz完美配合。实践建议业务侧的时间对象在入库前统一调用toISOString()从库中读出后凡是需要对外暴露的时间字段也一律以toISOString()产出的 ISO-8601 UTC 字符串为准避免在领域层引入本地时区。三、禁止在循环内执行数据库查询N1改用批量查询 / JOIN / IN / ANY约定原文Do not run DB queries inside loops (N1). Prefer a single batched query, a join, or anIN/ANYlookup over per-item round-trips.N1 是 ORM/查询构建器项目中最典型的性能陷阱先查一个集合1 次查询再对每个元素单独查一次N 次查询网络往返与语句解析开销随 N 线性膨胀在 Agent 会话这类高频读写场景下会迅速拖垮吞吐。trueforge 的约定明确给出三条替代路径单条批量查询batched queryJOININ/ANY查找。源码中能找到直接印证。例如 PostgresAgentStore.ts 中按一组名字批量解析 agent 引用时使用了一条WHERE reference_name IN (...)的批量查询而非对每个名字单独 round-tripWHERE reference_name IN (${wanted})会话存储域则进一步封装了 Postgres 数组展开的辅助函数。在 session-store/sqlExpressions.ts 中可以看到unnest(ids::text[]) WITH ORDINALITY与LATERAL unnest(array_expr) WITH ORDINALITY两种形态——前者用于selectFrom场景把一次传入的 ID 数组展开成可参与 JOIN 的关系表后者用于 JOIN 场景实现一次查询拿到多个线程上下文的能力典型调用点见 session-store/queries/events.ts 与 session-store/queries/turns.ts。这些正是以单条语句替代循环查询的工程化落地把批量 ID 传进一条 SQL由数据库完成展开、关联与过滤。实践建议写任何需要先查集合、再逐项处理的逻辑前先自问能否把逐项处理合并进同一语句trueforge 的做法是优先用IN列表、ANY(array)或unnest WITH ORDINALITY与目标表做一次 JOIN让数据库在单轮往返内完成全部工作。四、迁移必须首行执行SET LOCAL lock_timeout 5s让等待中的 DDL 快速失败约定原文Postgres migrations MUST startup/downwithSET LOCAL lock_timeout 5sso waiting DDL fails fast instead of blocking later queries (includingSELECTs) behind it in the lock queue.这是本约定文件中工程味最浓的一条涉及 Postgres 的锁队列行为DDL如ALTER TABLE、CREATE INDEX需要获取排他锁若目标表正被长事务持有行锁/表锁DDL 会进入锁等待队列。默认lock_timeout为无限等待意味着这条 DDL 会一直挂着而排在它后面的普通查询包括SELECT也会被阻塞在锁队列里最终拖垮整个数据库。trueforge 的做法是在每个迁移的up/down函数体第一行执行await sqlSET LOCAL lock_timeout 5s.execute(db);SET LOCAL的作用域是当前事务事务结束自动失效不会污染后续语句5s则给 DDL 一个明确的等待上限——5 秒内拿不到锁就直接抛错、迁移失败而不是无限阻塞后续流量。仓库中每一个迁移文件都遵循这一模式例如 init 迁移 的up与down、session 存储迁移 的down中dropTable(...).cascade()之前以及 agent 迁移 的回滚分支。此外schema 引导事务 schema.ts 内部同样以SET LOCAL lock_timeout 5s开头并借助pg_advisory_xact_lock串行化多实例同时启动时的建 schema 竞争。实践建议凡是在线执行的迁移尤其是ALTER TABLE/CREATE INDEX/ADD CONSTRAINT这类会碰锁的语句一律在事务开头设置短lock_timeout同时保持迁移语句幂等IF NOT EXISTS/IF EXISTS这样 5 秒失败后重跑即可恢复不会留下半执行状态。五、所有业务表与 Kysely 迁移簿记都放在POSTGRES_SCHEMA默认trueforge非public约定原文All app tables and Kysely migration bookkeeping live in the configured Postgres schema (POSTGRES_SCHEMA, defaulttrueforge— notpublic). The init migration ownsCREATE SCHEMA(the Migrator also creates it formigrationTableSchema);createDbsetssearch_pathto that schema.这条规则回答了表建在哪里的问题可拆成四个机制点① schema 名来自环境变量默认trueforge。配置定义在 config.ts 中DEFAULT_POSTGRES_SCHEMA trueforge第 46 行并限定必须匹配小写标识符正则/^[a-z_][a-z0-9_]{0,62}$/第 48、253 行——schema 名必须是合法且可安全拼接进 SQL 的小写标识符防止注入与命名冲突。② init 迁移负责CREATE SCHEMA。仓库中编号最早20260727_000001的 init 迁移 在SET LOCAL lock_timeout之后执行await sqlCREATE SCHEMA IF NOT EXISTS ${sql.id(getTrueForgePostgresSchema())}.execute(db);同时其down被刻意设计为 no-op只执行SELECT 1——注释明确说明drop schema 会连 Kysely Migrator 自己的簿记表一起删掉因此不提供真正回滚。③ Kysely Migrator 的簿记表同样挂在自定义 schema 上。在 migratePostgres.ts 中Migrator 的migrationTableSchema显式传入getTrueForgePostgresSchema()因此kysely_migration与kysely_migration_lock这两张迁移状态表也落在trueforgeschema 而非public。迁移流程本身migrateToLatest会先调用ensureTrueforgeSchema引导 schema再执行migrateToLatest或按指定迁移名执行migrateTo。④createDb通过连接参数设置search_path。在 client.ts 中连接池创建时显式传入options: -c search_path${schema},这样同一条连接上所有的裸表名不带 schema 前缀的 SQL都会解析到trueforgeschema业务代码无需手工写trueforge.xxx前缀。此外schema.ts 中的ensureTrueforgeSchema还支持一个可配置项AUTOMATICALLY_MOVE_TRUEFORGE_TABLES_FROM_PUBLIC_TO_TRUEFORGE_SCHEMA默认开启见 config.ts 第 1005-1008 行首次引导时若发现旧版本遗留的public表列表见 schema.ts 的TABLES_TO_MOVE包括session、turn、agent、mcp_server、schedule、两张kysely_migration*簿记表等会自动ALTER TABLE ... SET SCHEMA迁入目标 schema让老实例平滑升级。本地实操在自定义 schema 上跑 Postgres 迁移真机验证这套机制的最快路径是仓库自带的编排文件 docker-compose.yml其中postgres服务使用postgres:17镜像第 7-8 行应用服务通过POSTGRES_HOST: postgres、POSTGRES_PORT: 5432等环境变量连接第 48-49 行。在packages/trueforge目录下执行迁移命令定义于 package.jsonpnpm migrate该脚本等价于cross-env NODE_OPTIONS--conditionstrueforge-dev STANDALONEfalse tsx --env-file.env src/db/postgres/migrate-cli.ts。关键约束见 migrate-cli.ts是必须设置STANDALONEfalse——standalone 模式下该脚本会直接抛错因为 standalone 模式走的是 SQLite其迁移在服务启动时自动执行需要提供DATABASE_URL或POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB/POSTGRES_HOST组合与必要的 Redis 环境变量连接参数由配置项驱动DATABASE_POOL_MAX、POSTGRES_STATEMENT_TIMEOUT_MS默认 60000ms见 config.ts 第 957-959 行、POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS与DATABASE_SSL均会在 createDb 中应用到连接池包括 10 秒的连接超时、每连接的statement_timeout、idle_in_transaction_session_timeout以及search_path设置。迁移执行后你可以用如下 SQL 验证五条约定全部生效-- 1) schema 与迁移簿记表都在 trueforge 而非 public SELECT nspname FROM pg_namespace WHERE nspname trueforge; SELECT schemaname, tablename FROM pg_tables WHERE tablename LIKE kysely_migration%; -- 2) 所有时间列都是 timestamptz SELECT column_name, data_type FROM information_schema.columns WHERE table_schema trueforge AND column_name IN (created_at, updated_at);六、配套工程细节连接池的类型解析与错误匹配在深入以上五条约定时client.ts 还提供了两个值得借鉴的配套实现bigint 数组类型解析。node-postgres 默认只把标量int8OID 20解析为number而bigint[]OID 1016不在默认覆盖范围内。trueforge 通过 configurePgTypeParsers 额外注册了int8[]解析器把 Postgres 数组字面量如{1,2,3}解析为number[]并且特意声明其安全边界为Number.MAX_SAFE_INTEGER2^53——这正好服务于turn_thread.context_ids这类bigint[]上下文引用列见 session 存储迁移。按 SQLSTATE 匹配数据库错误。由于instanceof DatabaseError在同时加载两份 pg-protocolCJSESM 版本偏差时会静默失效trueforge 采用按错误code字段匹配的方式见 isPgErrorCode / isUniqueViolation / isPgConstraint分别对应23505唯一约束冲突与命名约束匹配。这让 store 层可以在迁移或写入冲突时做出确定性响应而不是依赖脆弱的类型判断。总结一条可迁移到任何 Postgres 工程的开发纪律trueforge 用一份仅五条的 AGENTS.md把 Postgres 持久层最关键的正确性问题一次性钉死timestamptz消灭时区歧义、toISOString()统一应用层序列化、批量查询消灭 N1、lock_timeout 5s让迁移在锁竞争下快速失败、自定义 schema 隔离业务表与迁移簿记。每一条都不是纸面规范而是散落在 migrations、store 各子目录与 client.ts / schema.ts 中可逐行核验的实现事实。对希望在自己的服务中复刻这套实践的人最直接的落地点是所有时间列一律timestamptz 出入库统一toISOString()、查询全部批量化为IN/ANY/unnestJOIN、每个迁移事务首行SET LOCAL lock_timeout 5s、为应用单独建 schema 并把search_path固化在连接池上。这四板斧即可让绝大多数多租户、高写入的 Postgres 服务规避最常踩的时区错乱、锁队列阻塞与 N1 性能退化三类问题。赞分享【免费下载链接】trueforgeThe open-source agent harness - the runtime layer that turns an LLM into a working agent.项目地址https://gitcode.com/gh_mirrors/tr/trueforge点击查看免费下载相关推荐GPUStack 开发工程指南解读架构、代码规范与数据库迁移实践GPUStack 开发工程指南解读架构、代码规范与数据库迁移实践 GPUStack 是一个开源的 GPU 集群管理器用于高性能 AI 模型推理vLLM、S后端人工智能模型推理服务集群管理可观测性Box3D 移植到 Rust 的工程规范makepad-box3d 的 1:1 移植约定与浮点确定性实践Box3D 移植到 Rust 的工程规范makepad box3d 的 1:1 移植约定与浮点确定性实践 本篇指南系统讲解 makepad 仓库中 libs/前端UI组件3D渲染跨平台游戏开发Velero 全解析备份、恢复与迁移 Kubernetes 集群和持久卷的工程实践Velero 全解析备份、恢复与迁移 Kubernetes 集群和持久卷的工程实践 Velero前身为 Heptio Ark是一个为 Kubernetes云原生灾备存储后端上一篇【免费下载】 IET期刊投稿LaTeX模板科研工作者的得力助手下一篇如何永久保存微信聊天记录三步实现数据自由与AI训练创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

dbx 的 Pulsar 4.2 冒烟测试环境:public/default 命名空间与 dbx-smoke 主题实战

dbx 的 Pulsar 4.2 冒烟测试环境:public/default 命名空间与 dbx-smoke 主题实战

数据库开发者工具桌面应用CLIMCP 服务AI 应用 【免费下载链接】dbx 15MB,轻量级跨平台数据库客户端、数据库管理工具。支持 MySQL、PostgreSQL、SQLite、Redis、MongoDB、DuckDB、ClickHouse、SQL Server 等。15MB, lightweight, cross-platform database client. S…

2026/10/10 5:11:27 阅读更多 →
工业热图异常检测:ResNet三步改造与温度域落地实践

工业热图异常检测:ResNet三步改造与温度域落地实践

简介:本资源是一份面向深度学习与网络安全方向研究者、高校学生及工业界算法工程师的学术型技术文档,聚焦解决传统自编码器异常检测模型易过拟合、误报率高的核心痛点。文中提出基于ResNet残差网络的新型异常检测架构,通过固定切分数据为A/B两…

2026/10/10 5:11:27 阅读更多 →
@nteract/actions 动作系统指南:nteract 核心 SDK 中 Redux 动作与 action creator 的完整解析

@nteract/actions 动作系统指南:nteract 核心 SDK 中 Redux 动作与 action creator 的完整解析

开发工具数据科学 【免费下载链接】archived-desktop-app The old electron based nteract notebook 项目地址: https://gitcode.com/gh_mirrors/nt/archived-desktop-app 点击查看 免费下载 nteract/actions 是 nteract 核心 SDK 中负责定义 动作常量(…

2026/10/10 5:10:27 阅读更多 →

最新新闻

开源实时协作Markdown编辑器HedgeDoc:自托管与权限管理指南

开源实时协作Markdown编辑器HedgeDoc:自托管与权限管理指南

如果你所在的环境里,协作记录一直散落在聊天记录、本地文本和邮箱附件之间,我建议你认真了解一下 HedgeDoc。它是一款开源的、基于 Web 的实时协作 Markdown 编辑器,浏览器打开就能用,也能在自己的服务器上搭建。我把团队内部的技…

2026/10/10 5:44:39 阅读更多 →
变步长扰动观察法光伏MPPT仿真:S-Function与Boost电路实践

变步长扰动观察法光伏MPPT仿真:S-Function与Boost电路实践

上次接了个仿真任务,要求搭一套能随光照强度突变“时刻跟踪”最大功率点的光伏MPPT模型。原以为Simulink里找一个现成模块拖进去就行,结果翻遍标准库也没找到变步长扰动观察法仿真模型,最后老老实实把算法写进s-function模块,配合…

2026/10/10 5:44:39 阅读更多 →
AnyPS5远程串流全攻略:从局域网到广域网,低延迟玩转PS5

AnyPS5远程串流全攻略:从局域网到广域网,低延迟玩转PS5

1. 从“AnyPS5”这个标题说起:它到底想解决什么问题第一次看到“AnyPS5”这个标题,我脑子里蹦出来的第一反应是:这大概率是一个围绕“跨平台串流”或者“远程访问”做文章的项目。为什么这么判断?因为“Any”这个前缀在技术圈里几…

2026/10/10 5:44:39 阅读更多 →
Zeek 证书透明度验证指南:深入解析 validate-sct.zeek 的 SCT 校验机制

Zeek 证书透明度验证指南:深入解析 validate-sct.zeek 的 SCT 校验机制

网络安全网络IDS 【免费下载链接】zeek Zeek is a powerful network analysis framework that is much different from the typical IDS you may know. 项目地址: https://gitcode.com/gh_mirrors/ze/zeek 点击查看 免费下载 导读 本文围绕 Zeek 的 policy/protoc…

2026/10/10 5:44:39 阅读更多 →
YCBlogs 开源项目全景导览:Android 组件封装库、视频播放器、线程池与多渠道打包实战指南

YCBlogs 开源项目全景导览:Android 组件封装库、视频播放器、线程池与多渠道打包实战指南

教程技术博客文档 【免费下载链接】YCBlogs 技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分fl…

2026/10/10 5:44:39 阅读更多 →
项目成本管理实战:从估算到挣值管理的全流程解析

项目成本管理实战:从估算到挣值管理的全流程解析

1. 先搞清楚:项目成本管理到底在管什么很多人一听到"项目成本管理",第一反应就是"省钱"。特别是当它作为教材里的第11章出现时,很容易被理解成一套记账、算账、省钱的流程。但实际上,项目成本管理的核心不是&…

2026/10/10 5:43:39 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →