
Encore Go 应用中的 PostgreSQL 数据库供给、迁移、查询与连接实战指南【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encoreEncore 将 SQL 数据库视为一种逻辑资源你只需在代码中声明数据库并写好迁移文件本地encore run与云端部署都会自动完成集群创建、Schema 迁移与连接注入。本篇指南以官方文档为基础结合本仓库源码运行时库、CLI 与守护进程实现完整讲解如何在 Encore Go 服务中创建数据库、编写迁移、执行增删改查、连接外部工具并深入剖析encore_services角色与迁移出错时的处理机制。读完本文你将能够在任意 Encore 服务中用两行代码声明一个 PostgreSQL 数据库用版本化迁移文件管理 Schema用与database/sql几乎一致的 API 读写数据通过encore db系列命令从外部连接数据库并在迁移失败时安全地恢复。数据库即逻辑资源Encore 的核心抽象在 Encore 中SQL 数据库是应用声明的一部分而不是需要你手工运维的外部依赖。原生支持PostgreSQL。声明数据库后Encore 的静态分析器会识别它并负责在本地开发环境用 Docker 自动拉起 PostgreSQL 集群在云端按环境类型自动供给合适的数据库服务自动应用迁移文件、注入连接凭证并把每次查询接入分布式追踪。这种声明式资源模型意味着你的代码里没有连接串硬编码、没有环境判断分支——同一份代码在本地、开发环境和生产环境都能直接运行。创建数据库sqldb.NewDatabase创建数据库的第一步是在服务包内导入encore.dev/storage/sqldb调用sqldb.NewDatabase并把返回值赋给包级变量。数据库必须在某个 Encore 服务 中创建。官方示例todo服务-- todo/db.go -- package todo // Create the todo database and assign it to the tododb variable var tododb sqldb.NewDatabase(todo, sqldb.DatabaseConfig{ Migrations: ./migrations, }) // Then, query the database using db.QueryRow, db.Exec, etc. -- todo/migrations/1_create_table.up.sql -- CREATE TABLE todo_item ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, done BOOLEAN NOT NULL DEFAULT false -- etc... );sqldb.DatabaseConfig中只有Migrations一个字段用于指定存放迁移文件的目录——这是你定义数据库 Schema 的唯一入口。从源码看NewDatabase的定义位于 runtimes/go/storage/sqldb/pkgfn.gofunc NewDatabase(name string, config DatabaseConfig) *Database { return Singleton.GetDB(name) }这里有几个对使用者至关重要的约束全部记录在源码注释中参数必须是常量字面量Encore 依赖静态分析识别数据库及其配置因此name和config不能是运行时计算出来的值只能在包级变量声明处调用在函数体内调用NewDatabase会直接导致编译错误命名规范数据库名必须在整个应用内唯一且使用 kebab-case小写字母数字加连字符命名不可更改一旦创建并部署切勿修改数据库名否则 Encore 会认为你声明了一个全新数据库并重新创建。本地运行的前提Docker本地环境下执行encore run时 Encore 会用 Docker 自动创建数据库集群。因此运行前请确保 Docker 已安装并处于运行状态。一个常见的坑如果你的应用已经在运行再新增一个数据库定义需要停止并重启encore runEncore 才会用 Docker 把新数据库创建出来。本地 Docker 驱动的实现位于 cli/daemon/sqldb/docker/docker.go它使用官方镜像encoredotdev/postgres:18见该文件中的Image常量启动容器时设置POSTGRES_USERpostgres、POSTGRES_PASSWORDpostgres并通过数据卷持久化数据。CheckRequirements方法会在 Docker 不存在或守护进程未启动时给出明确报错This application requires docker to run since it uses an SQL database.。数据卷按应用 ID 命名空间命名保证不同应用、不同命名空间之间的数据相互隔离。数据库迁移定义 Schema 的版本化方式命名约定迁移文件必须遵循严格的命名规范以数字开头后跟下划线_数字必须依次递增文件名必须以.up.sql结尾示例1_first_migration.up.sql、2_second_migration.up.sql、3_migration_name.up.sql。为了让编辑器里的排序更美观可以用前导零补位例如0001_migration.up.sql。up 与 down 迁移Encore自动执行up迁移按顺序依次应用每个迁移表达从上一次迁移到当前状态的增量变化而down迁移必须手动执行Encore 不负责回滚 Schema。迁移目录结构迁移文件位于服务包内的migrations目录中每个文件命名为number_name.up.sql。典型目录结构如下/my-app ├── encore.app // ... and other top-level project files │ └── todo // todo service (a Go package) ├── migrations // todo service db migrations (directory) │ ├── 1_create_table.up.sql // todo service db migration │ └── 2_add_field.up.sql // todo service db migration ├── todo.go // todo service code └── todo_test.go // tests for todo service首个迁移通常定义初始表结构例如todo/migrations/1_create_table.up.sqlCREATE TABLE todo_item ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, done BOOLEAN NOT NULL DEFAULT false );迁移引擎的源码细节迁移执行逻辑位于 cli/daemon/sqldb/migrate.go。有两点值得了解非顺序迁移支持Encore 的NonSequentialMigrator基于 go-migrate 库扩展而来不要求迁移编号连续而是通过读取schema_migrations表中已应用的版本号来决定下一个要执行的迁移。因此你可以在历史迁移文件中间插入一个新迁移编号取中间值Encore 只会应用那些尚未执行的。同语句标记成功ReadUp会把insert into schema_migrations (version, dirty) values (...)直接拼接到迁移语句末尾在同一个事务/语句中完成执行迁移 标记成功避免出现迁移已执行但标记失败的脏状态。插入数据与database/sql一致的 API声明数据库之后即可通过tododb变量上的方法操作数据。接口风格与 Go 标准库database/sql高度一致参考 runtimes/go/storage/sqldb 包。一种常见的做法是写一个使用Exec的辅助函数。例如向上面的示例 Schema 插入一条 todo 记录-- todo/insert.go -- // insert inserts a todo item into the database. func insert(ctx context.Context, id, title string, done bool) error { _, err : tododb.Exec(ctx, INSERT INTO todo_item (id, title, done) VALUES ($1, $2, $3) , id, title, done) return err }注意 SQL 中的占位符使用 PostgreSQL 风格的位置参数$1、$2、$3。除了在Database对象上调用方法sqldb包还提供了对应的包级函数sqldb.Exec、sqldb.Query、sqldb.QueryRow、sqldb.Begin见 pkgfn.go它们会自动路由到当前服务所对应的数据库通过getCurrentDB()实现适合在无需显式持有数据库变量的场景使用。从底层实现看runtimes/go/storage/sqldb/db.goDatabase.Exec基于pgxpool.Pool执行默认连接池上限为30cfg.MaxConns 30可由MaxConnections配置覆盖并在执行前后向分布式追踪系统写入DBQueryStart/DBQueryEnd事件——这意味着你在 Encore 开发面板里看到的每条 SQL 查询追踪都来自这一层埋点。查询数据QueryRow、Scan 与错误处理查询同样需要导入encore.dev/storage/sqldb在服务包或其子包中。例如读取上面示例 Schema 中的一条 todovar item struct { ID int64 Title string Done bool } err : tododb.QueryRow(ctx, SELECT id, title, done FROM todo_item LIMIT 1 ).Scan(item.ID, item.Title, item.Done)QueryRow预期最多返回一行。当没有匹配行时它会报告一个错误你应当导入标准库errors包用errors.Is(err, sqldb.ErrNoRows)来判别。ErrNoRows定义在 sqldb.go 中本质就是sql.ErrNoRows。错误分类与结构化错误底层实现里数据库服务器返回的错误会被统一转换为结构化的sqldb.Error见 runtimes/go/storage/sqldb/errors.go其中包含Code错误大类对应sqlerr.Code例如外键冲突、唯一约束冲突等Severity错误严重级别DatabaseCode数据库服务器特定的 SQLSTATE 错误码Message可读的错误消息SchemaName/TableName/ColumnName/DataTypeName/ConstraintName当错误与某个数据库对象关联时提供。你可以用sqldb.ErrCode(err)提取错误大类实现精细化的业务错误处理例如将唯一约束冲突映射为 HTTP 409。同时convertErr会把无行、事务关闭、超时、取消等情况分别包装为对应的 Encore 错误类别NotFound、Internal、DeadlineExceeded、Canceled 等便于与 beta/errs 的错误体系无缝衔接。事务与底层驱动访问需要事务时使用tododb.Begin(ctx)返回的Tx提供Exec、Query、QueryRow、Commit、Rollback方法见 sqldb.go语义与database/sql.Tx一致。如果你需要把连接交给第三方库例如 ORM 或专门工具tododb.Stdlib()返回一个连到同一数据库的*sql.DB可直接用于sqlx、GORM 等期望*sql.DB的库db.gosqldb.Driver*pgxpool.Pool)以类型安全的方式直接取出底层*pgxpool.Poolsqldb.DriverConn(conn, func(driverConn *pgx.Conn) error {...})让你在*sql.Conn上安全地访问底层*pgx.Conn。数据库的自动供给本地与云端Encore 会自动按应用需求供给数据库——当你定义数据库后下一次部署时 Encore 就会完成供给。供给方式因环境而异本地开发使用 Docker 创建一个数据库集群见上文本地运行的前提小节云端生产环境production通过所选云厂商的托管 SQL 数据库服务供给云端开发环境development以Kubernetes Deployment 持久化磁盘的方式供给。不同云厂商、不同环境类型下具体供给的基础设施细节参见 基础设施文档。环境类型的概念详见 环境说明。连接数据库从应用外部访问有时你需要从后端应用之外连接数据库——例如跑脚本、临时查询、导出数据做分析。Encore 不会在本地环境或 Encore Cloud 环境中暴露数据库用户凭据但提供了更安全便捷的连接字符串方案。如果需要把外部工具如数据管道、BI 平台接入数据库关于 SSL 证书与网络访问的指引参见 连接外部工具到数据库。使用 Encore CLIEncore CLI 内置了三种连接数据库的方式命令实现见 cli/cmd/encore/db.goencore db shell database-name [--envname]打开一个 psql 交互式 shell 连接到指定环境中的database-name数据库。省略--env时默认连接本地开发环境。默认以只读权限连接可用以下标志提升权限三者互斥标志连接权限--write读写权限--admin管理员权限--superuser超级用户权限从源码看dbShellCmd会优先使用本机$PATH中的psql若未安装则回退到用docker run拉起容器内的 psql在 macOS/Windows 上会自动把连接地址中的localhost/127.0.0.1替换为host.docker.internal以适配 Docker 网络。此外还支持--test连接集成测试数据库与--shadow连接影子数据库用于 Prisma 等工具做漂移检测二者都隐含--envlocal。如果不指定数据库名CLI 会在当前目录向上寻找包含migrations目录的服务并自动推断数据库名支持目录自动补全。encore db conn-uri database-name [--envname]输出一条数据库连接字符串。指定云端环境时返回的连接字符串是临时的。省略--env默认输出本地开发环境的连接串。encore db proxy [--envname]建立一个本地代理把进入的连接转发到指定环境的数据库。省略--env默认连接本地开发环境。可用--port指定监听端口默认为随机端口。更多数据库管理命令如encore db reset参见encore help db。使用数据库用户凭据AWS/GCP对于 AWS/GCP 上的云端环境可以查看 Encore 供给数据库时创建的用户凭据打开 Encore Cloud 控制台中的应用进入对应环境的Infrastructure页面在相关Database Cluster的USERS区块中找到。本地环境与 Encore Cloud 环境不提供此功能。处理迁移错误Encore 应用迁移时迁移不一定是干净的失败原因可能包括迁移文件中的 SQL 语法错误试图添加UNIQUE约束但表中现有数据并不唯一现有数据库 Schema 与预期不符要修改的数据库对象实际不存在以及其他各种原因。一旦失败Encore 会回滚该迁移如果发生在云端部署期间整个部署会被中止。修复问题后本地重新执行encore run云端推送更新后的代码即可重试。schema_migrations表Encore 通过schema_migrations表跟踪已应用的迁移database# \d schema_migrations Table public.schema_migrations Column | Type | Collation | Nullable | Default ------------------------------------------------ version | bigint | | not null | dirty | boolean | | not null | Indexes: schema_migrations_pkey PRIMARY KEY, btree (version)version列记录最后一次应用的迁移版本。如果想跳过某个迁移或重新执行某个迁移直接修改这一列的值即可。例如要重跑最后一个迁移执行UPDATE schema_migrations SET version version - 1;注意Encore 默认不使用dirty标志从 migrate.go 的SetVersion实现可以看到PSQL 下迁移在同一事务内执行失败会自动回滚因此无需标记 dirty。encore_services角色连接迁移与运行时的权限桥Encore 使用一个共享数据库角色encore_services在迁移执行的权限与服务运行时连接数据库的权限之间搭建桥梁。这个角色在所有环境中都存在——本地和云端一致。它的工作方式非常简洁所有执行迁移的角色都被授予encore_services所有服务运行时连接数据库的角色也都被授予encore_services。这意味着任何由encore_services拥有的对象或授予encore_services的权限迁移角色和运行时服务角色都能自动访问。你可以利用迁移脚本给encore_services授予额外权限这些权限会被运行时的服务角色继承。从 cli/daemon/sqldb/cluster.go 的集群角色创建逻辑可以看到具体实现迁移角色encore-migrator被授予GRANT encore_services TO ... WITH ADMIN OPTION带管理员选项使其可以把角色转授出去而服务角色encore-service被授予GRANT encore_services TO ...仅继承。此外 PostgreSQL 14 环境下还会创建encore-read、encore-write等预定义角色并授予pg_read_all_data/pg_write_all_data系统预置角色权限。物化视图示例物化视图是encore_services最有价值的应用场景之一它在迁移期间创建但需要在运行时由服务角色刷新。通过创建专用属主角色 → 授予encore_services→ 在创建视图时切换为该角色就能把这一缺口补上-- In a migration file CREATE ROLE matview_owner; GRANT matview_owner TO encore_services; SET ROLE matview_owner; CREATE MATERIALIZED VIEW my_view AS SELECT id, count(*) AS total FROM todo_item GROUP BY id; RESET ROLE;运行时服务角色通过encore_services继承来的权限即可扮演matview_owner并刷新视图_, err : tododb.Exec(ctx, SET ROLE matview_owner) _, err tododb.Exec(ctx, REFRESH MATERIALIZED VIEW my_view) _, err tododb.Exec(ctx, RESET ROLE)旧版行为Legacy在 Encore 早期版本中迁移使用与服务运行时同一个数据库账号执行。如需恢复这一旧行为设置以下环境变量ENCOREDEBUGsqldbrolelegacy该选项仅为向后兼容而保留新应用不建议使用。小结与推荐阅读至此你已经掌握了 Encore Go 应用中 SQL 数据库的完整使用闭环声明用sqldb.NewDatabaseDatabaseConfig{Migrations}声明数据库运行时入口建模用编号递增的.up.sql迁移文件定义 Schema读写用Exec/Query/QueryRow/Begin操作数据运行时实现供给与连接本地自动用 Docker 拉起Docker 驱动云端按环境类型自动供给用encore db系列命令从外部连接CLI 实现排障通过schema_migrations表处理迁移失败利用encore_services角色打通迁移与运行时的权限。如果你想进一步深入推荐阅读仓库中的相关文档服务概念、数据库迁移与 Schema 变更、共享数据库、连接现有数据库以及包含完整 SQL 数据库示例的 uptime 教程。【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考