#postgresql #database-migrations #database-server #migration #cli-tool

bin+lib renovate

一种新的处理Postgres架构迁移的方法

4个版本

0.2.23 2023年1月10日
0.2.22 2023年1月10日
0.2.21 2023年1月9日
0.2.19 2023年1月9日

2261数据库接口

每月37次下载

MIT 许可证

3MB
4.5K SLoC

Renovate:一种新的处理Postgres架构迁移的方法

随着支持的产品随时间演变,数据库架构设计也会演变。能够安全可靠地将架构迁移到新状态以纳入产品更改是很重要的。

传统上,ActiveRecord或sqlx之类的迁移系统允许您编写迁移文件来描述您想要执行的操作,以将数据库从当前状态移动到新状态。例如,如果您想向todos表添加一个created_at列,您需要先编写一个迁移脚本

ALTER TABLE todos ADD COLUMN created_at timestamptz DEFAULT NOW();

系统将使用此类迁移表来跟踪已应用的迁移文件

CREATE TABLE public._sqlx_migrations (
    version bigint NOT NULL PRIMARY KEY,
    description text NOT NULL,
    installed_on timestamp WITH time zone DEFAULT NOW() NOT NULL,
    success boolean NOT NULL,
    CHECKSUM bytea NOT NULL,
    execution_time bigint NOT NULL
);

当您成功应用上述迁移脚本时,将在迁移表中添加一条新记录,以指示它已被应用。下次运行迁移时,系统将检查迁移表,如果迁移脚本之前已应用,则会跳过迁移脚本。

这种方法是可靠的,但它将编写迁移脚本的责任推迟给了开发者。有时迁移脚本不易编写,尤其是在架构复杂且更改不直接时。审查迁移脚本也很困难,因为审查者需要了解当前状态以及希望达到的新状态。

较新的迁移系统,如atlas,可以理解数据库架构,并根据其理解为您生成迁移。为了实现这一点,通常系统需要了解用户想要过渡到的本地状态和数据库中当前运行的远程状态。一旦系统收集到这两种状态,它就可以通过diff它们来决定需要哪些更改。这种方法在管理云资源的工具中得到广泛应用,例如,terraform。它对开发者来说更方便,而像terraform这样的工具已经证明了它们的可靠性和安全性。

Renovate是一个属于第二类别的工具。与atlas不同,它不使用新的语言(例如HCL)来描述本地状态。相反,它使用现有的SQL DDL来描述模式状态。您可以使用renovate schema init从现有的数据库开始一个新项目。Renovate将从数据库服务器检索模式(如果有),并将SQL文件正确地组织在新创建的git仓库的不同文件夹中。这将是您的本地状态。您可以对其进行任何您想要的修改。一旦您对模式满意,您可以使用renovate schema plan来获取迁移计划。Renovate将使用pg_dump从数据库服务器检索远程状态,然后比较本地状态和远程状态之间的AST以找到正确的迁移计划。如果您对迁移计划满意,您可以通过renovate schema apply将其应用到数据库服务器。

使用Renovate的好处

  1. 声明式模式定义。
  2. 只需使用SQL DDL。您不需要学习新语言。
  3. Renovate在引入工具时不会限制您。您可以使用它来启动一个新的数据库项目,或者您可以使用它来迁移现有的数据库项目。您不需要从头开始。您不需要对现有数据库进行任何修改。
  4. Renovate不会限制您使用什么工具。当您使用Renovate迁移数据库模式时,请随意使用其他工具或方法。您只需要执行简单的renovate schema fetch来更新本地状态,每次您在Renovate之外更新数据库模式时。然后您就可以继续了。不需要修改。

以下是一个示例

示例

 renovate schema init postgres://127.0.0.1:5432/test
 cat public/04_tables.sql
CREATE TABLE public.todos (title text, completed boolean);
 cat > public/04_tables.sql
CREATE TABLE public.todos (title text, completed boolean, created_at timestamptz default now());
 renovate schema plan
Table public.todos is changed:

1        |-CREATE TABLE public.todos (title text, completed boolean)
    1    |+CREATE TABLE public.todos (
    2    |+    title text,
    3    |+    completed boolean,
    4    |+    created_at timestamptz DEFAULT NOW()
    5    |+)

The following SQLs will be applied:

  ALTER TABLE public.todos ADD COLUMN created_at timestamptz DEFAULT NOW();

如果您受到了启发,这里有一个更详细的演示

demo

警告:此项目仍缺少许多功能。它尚未准备好投入生产使用。请注意,一些生成的迁移(例如更改复合类型中的字段)在此时应用是不安全的。如果您对这些迁移有更好的想法,请提交问题。

工作原理

在内部,Renovate使用pg_query将Postgres SQL DDL解析为AST,并使用pg_dump从数据库服务器检索远程状态。以下图显示了Renovate的工作流程

arch

有关更多信息,请参阅初步想法。或者您也可以查看架构

安装

目前,Renovate仅支持从源安装

$ cargo install renovate

Renovate CLI

$ renovate schema
? 2
renovate-schema[..]
Schema migration

USAGE:
    renovate schema [OPTIONS] <SUBCOMMAND>

OPTIONS:
        --drop-on-exit    drop database on exit (for testing purpose only)
    -h, --help            Print help information

SUBCOMMANDS:
    apply        apply the migration plan to the remote database server
    fetch        fetch the most recent schema from the remote database server
    help         Print this message or the help of the given subcommand(s)
    init         init a database migration repo
    normalize    normalize local schema via a temp local database
    plan         diff the local change and remote state, then make a migration plan

本地状态的文件夹结构

一旦您运行renovate schema init,它将创建一个基于从数据库服务器检索到的内容的git仓库,其文件夹结构如下所示

 tree
.
├── public
   └── 04_tables.sql
├── renovate.yml
└── rsvp
    ├── 02_enums.sql
    ├── 03_sequences.sql
    ├── 04_tables.sql
    └── 07_functions.sql

2 directories, 6 files

如您所见,文件夹结构是按模式名称组织的。文件以前缀数字命名,以指示执行顺序。例如,如果您有一个表todos和一个表rsvp.users,则public/04_tables.sql将包含todos,而rsvp/04_tables.sql将包含rsvp.users

Renovate对文件布局有很强的观点。如果您创建了一个包含以下SQL代码的随机文件(或使用任何现有的SQL文件)

CREATE TABLE hello.world (title text, completed boolean);

一旦您执行了 renovate schema apply 来应用更改,Renovate 将会从文件中移除代码,并将其移动到 hello/04_tables.sql(同时删除任何不必要的文件),因为这是它的正确位置。

已支持的功能

  • 类型
    • 复合类型添加/删除
    • 复合类型更改(仅限于破坏性更改)
    • 枚举类型添加/删除
    • 枚举类型添加值
    • 枚举类型重命名值(每次仅限于重命名一个)
    • 枚举类型更改值(仅限于破坏性更改)
    • 列添加/删除
    • 列类型更改
    • 列约束更改(默认、非空、唯一、检查)
    • 表约束添加/删除/更改
    • 表索引添加/删除/更改
    • 表触发器添加/删除/更改
    • 表RLS
    • 表策略添加/删除/更改
    • 表所有者更改
  • 视图添加/删除/更改
  • 物化视图添加/删除/更改
  • 函数添加/删除/更改
  • 序列添加/删除/更改
  • 权限添加/删除/更改

常见问题解答(FAQ)

Q: 如何使用 Renovate 撤销我的模式更改?

A: 与传统的模式迁移工具不同,Renovate 没有撤销的概念。您只需将模式更改回所需状态(例如,执行 git reset),然后运行 renovate schema plan 以获取迁移计划,就像平常一样。然后您可以将迁移计划应用到远程数据库服务器上。

Q: 如果我的模式更改不支持怎么办?

A: 请提交一个问题让我们知道。我们会尽快支持它。同时,您可以将更改手动应用到远程数据库中,然后运行 renovate schema fetch 来更新本地状态。

Q: 我能否将 Renovate 作为库使用?

A: 是的,您可以将它作为项目依赖项包含。如果您只想使用核心功能,请排除 cli 功能。但在此阶段不建议将其作为库使用。API 仍然不稳定。

Q: Renovate 的计划或路线图是什么?

A: 我目前没有路线图。我需要从您那里获取尽可能多的反馈。目前,我的优先级是使其稳定和可靠。截至写作时,项目已有相当数量的单元测试(57 个单元测试 + 1 个 CLI 测试),但仍缺乏许多场景的覆盖率。我还没有制作任何用户指南,这很重要。

许可证

Renovate 在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE

依赖项

~66MB
~1.5M SLoC