内容提要
该文提出将数据库部署控制权从外部迁移工具转移至项目自有的SQL程序。通过让工具仅准备PostgreSQL会话并将项目文件作为数据加载,部署策略(如执行顺序、事务边界、测试门禁)由项目用SQL定义,从而避免依赖工具特性。此方法简化了复杂部署,但要求团队掌握PL/pgSQL,并需直接连接以维持会话状态。
延伸解读
控制权反转的核心
文章提出将部署控制权从外部迁移工具转移到项目自身的SQL程序中。工具只负责准备会话和加载文件,而执行顺序、事务边界、测试门禁等策略均由项目用SQL定义。这种反转使得部署策略成为项目代码的一部分,可审查、可修改,不再依赖工具特性。
适用场景与代价
该方案适合部署有复杂形状(如阶段、条件、门禁)的项目,但对于简单的线性迁移,传统工具如Flyway更合适。采用此方案需要团队掌握PL/pgSQL,并需直接连接或会话级连接池,因为会话状态是机制核心。同时,部署历史需自行维护,错误处理也依赖PostgreSQL原生错误。
会话API与预处理
工具提供小型会话API,包括文件视图、参数视图、计划视图等,并有两个预处理点:测试宏展开和头尾分割。测试宏将CALL pgmi_test()展开为带保存点的SQL,头尾分割根据第一个顶层事务终止符将部署脚本分为原子头部和逐语句尾部,以支持非事务性操作如CREATE INDEX CONCURRENTLY。
Q&A
什么是“部署即PostgreSQL程序”的核心思想?
核心思想是将部署控制权从外部迁移工具转移到项目自有的SQL程序。工具只负责准备一个PostgreSQL会话,并将项目文件作为数据加载,而部署策略(如执行顺序、事务边界、测试门禁)由项目用SQL定义,从而避免依赖工具特性。
外部迁移工具通常需要承担哪些职责?
外部迁移工具需要承担三个主要职责:识别语句边界(因为需要单独执行语句或分类)、决定事务上下文(在迁移SQL运行前决定是否开启事务)、维护已运行的历史记录(通过历史表记录,但可能与实际数据库状态不一致)。
部署策略如何从工具转移到项目?
通过让工具准备一个PostgreSQL会话,将项目文件作为数据加载,然后执行一个项目拥有的SQL程序(如deploy.sql)。该程序可以控制执行顺序、事务边界和测试门禁,因为这些都变成了SQL语句,而不是工具配置。
pgmi的会话API提供了哪些功能?
pgmi的会话API包括:将项目文件加载为临时表并通过视图暴露(包含路径、内容、校验和等);参数可通过视图和会话设置访问;执行计划是一个临时视图,可查询和断言;测试文件单独加载,通过CALL pgmi_test()执行,支持保存点隔离。
为什么执行计划是一个视图而不是列表?
因为视图是可查询的,项目可以在执行前对计划进行断言(如检查排序键冲突);视图是派生的,一个文件可以贡献多个执行行(如幂等文件多次运行);并且排序使用COLLATE "C",避免服务器区域设置导致计划漂移。
采用这种反转方法有哪些成本或缺点?
成本包括:需要自己编写原本由工具提供的编排逻辑(如排序、失败处理);对于简单线性迁移,可能不如传统工具高效;需要自己维护历史记录(否则没有);需要直接连接或会话池,因为事务池会破坏会话状态;所有操作通过一个连接,不适合大规模数据加载;错误处理更原始,没有迁移特定的错误分类。
pgmi如何支持非事务性操作(如CREATE INDEX CONCURRENTLY)?
pgmi将deploy.sql分为头部和尾部:头部在第一个顶层事务终止符之前,作为一个整体发送,在事务中执行;终止符之后的部分逐条发送,在自动提交模式下执行,从而允许CREATE INDEX CONCURRENTLY等非事务性操作。
文章提到的“测试门禁”是如何实现的?
测试门禁通过将CALL pgmi_test()放在COMMIT之前实现。测试在同一个事务中运行,如果任何测试失败,事务回滚,部署不会提交。这确保了只有所有测试通过,部署才会生效。