shipmonk/doctrine-two-phase-migrations

Doctrine ORM 的两阶段迁移:在部署新代码库版本前后

0.3.0 2024-08-19 12:49 UTC

This package is auto-updated.

Last update: 2024-09-02 11:53:07 UTC


README

这个轻量级库允许你在集群环境(如 kubernetes)的部署过程中执行更安全的 Doctrine 迁移,其中 rolling-update 会发生。每个迁移有两个 up 阶段,没有 down 阶段。

  • before
    • 在任何流量到达新应用程序版本之前调用
    • 通常包含 ADD COLUMN 等。
  • after
    • 在部署完成后且没有流量到达旧应用程序版本时调用
    • 通常包含 DROP COLUMN 等。

您可以在 YouTube 上查看有关此库的捷克语演讲:Czech talk about this library on YouTube

安装

composer require shipmonk/doctrine-two-phase-migrations

在 symfony 应用程序中的配置

如果您的 Doctrine\ORM\EntityManagerInterface 是自动注入的,只需在您的 DIC 中注册少量服务并标记命令即可

_instanceof:
    Symfony\Component\Console\Command\Command:
        tags:
            - console.command

services:
    ShipMonk\Doctrine\Migration\Command\MigrationInitCommand:
    ShipMonk\Doctrine\Migration\Command\MigrationRunCommand:
    ShipMonk\Doctrine\Migration\Command\MigrationSkipCommand:
    ShipMonk\Doctrine\Migration\Command\MigrationCheckCommand:
    ShipMonk\Doctrine\Migration\Command\MigrationGenerateCommand:
    ShipMonk\Doctrine\Migration\MigrationService:
    ShipMonk\Doctrine\Migration\MigrationConfig:
        $migrationsDir: "%kernel.project_dir%/migrations"

    # more optional parameters:
        $migrationClassNamespace: 'YourCompany\Migrations'
        $migrationTableName: 'doctrine_migration'
        $migrationClassPrefix: 'Migration' # will be appended with date('YmDHis') by default
        $excludedTables: ['my_tmp_table'] # migration table ($migrationTableName) is always added to excluded tables automatically
        $templateFilePath: "%kernel.project_dir%/migrations/my-template.txt" # customizable according to your coding style
        $templateIndent: "\t\t" # defaults to spaces

命令

初始化

安装后,您需要在您的数据库中创建 migration 表。即使该表已被初始化,运行它也是安全的。

$ bin/console migration:init

# example output:
Creating migration table... done.

生成新迁移

您可以自动从数据库 <=> 实体差异生成迁移。这将所有由 Doctrine 生成的查询都放在“before”阶段,这对任何破坏性操作都是不正确的。请务必验证迁移并将查询移动到适当的阶段或调整它们。当没有检测到差异时,将生成空迁移类。

$ bin/console migration:generate

# example output:
Migration version 20230217063818 was generated

生成的文件看起来像这样

<?php declare(strict_types = 1);

namespace App\Migrations;

use ShipMonk\Doctrine\Migration\Migration;
use ShipMonk\Doctrine\Migration\MigrationExecutor;

class Migration20230217063818 implements Migration
{

    public function before(MigrationExecutor $executor): void
    {
        $executor->executeQuery('CREATE INDEX IDX_542819F35080ECDE ON my_table (my_column)');
    }

    public function after(MigrationExecutor $executor): void
    {
    }

}

您可以通过向 MigrationConfig 提供 $templateFilePath 自定义模板来调整它,但它需要实现 Migration 接口。

状态验证

您可以检查等待迁移和实体同步状态

$ bin/console migration:check

# example success output:
Phase before fully executed, no awaiting migrations
Phase after fully executed, no awaiting migrations
Database is synced with entities, no migration needed.
$ bin/console migration:check

# example failure output:
Phase before fully executed, no awaiting migrations
Phase after has executed migrations not present in /app/migrations: 20220208123456
Database is not synced with entities, missing updates:
 > DROP INDEX IDX_9DA1A2026EA0B6CA ON my_table

跳过所有迁移

您还可以将所有迁移标记为已执行,例如,当您仅从实体创建新鲜架构时。这将标记所有未执行的迁移在所有阶段已迁移。

$ bin/console migration:skip

# example output:
Migration 20230214154154 phase after skipped.
Migration 20230214155401 phase after skipped.
Migration 20230215050511 phase after skipped.
Migration 20230217061357 phase after skipped.

执行迁移

执行不会进行任何交互,如果没有迁移要执行,则不会失败或警告。

$ bin/console migration:run before

# example output:
Executing migration 20220224045126 phase before... done, 0.032 s elapsed.
Executing migration 20220224081809 phase before... done, 0.019 s elapsed.
Executing migration 20220224114846 phase before... done, 0.015 s elapsed.

$ bin/console migration:run after

# example output:
Executing migration 20220224045126 phase after... done, 0.033 s elapsed.
Executing migration 20220224081809 phase after... done, 0.006 s elapsed.
Executing migration 20220224114846 phase after... done, 0.000 s elapsed.

在执行所有迁移(例如在测试环境中)时,您可能希望实现逐个执行。您可以通过

$ bin/console migration:run both

# example output:
Executing migration 20220224045126 phase before... done, 0.032 s elapsed.
Executing migration 20220224045126 phase after... done, 0.033 s elapsed.
Executing migration 20220224081809 phase before... done, 0.019 s elapsed.
Executing migration 20220224081809 phase after... done, 0.006 s elapsed.

高级用法

为每个执行的查询运行自定义代码

您可以通过实现 MigrationExecutor 接口并将实现注册为服务来挂钩迁移执行。实现 executeQuery() 以在每次查询之前/之后运行检查或其他代码。此方法接口模仿了 Doctrine\DBAL\Connection::executeQuery() 的接口。

在事务中运行所有查询

您可以将模板(或单个迁移)更改为扩展; TransactionalMigration。这会导致每个阶段都在迁移中执行。请注意,许多数据库(如 MySQL)不支持 DDL 操作(ALTER 等)的事务。

检查执行持续时间

迁移表有 started_atfinished_at 列,包含带有微秒的 datetime 数据。但是,这些列默认声明为 VARCHAR,因为 doctrine/dbal 还不支持微秒。这可能使 datetime 操作(如持续时间计算)复杂化。您可以在某些迁移中手动调整结构以满足您的需求(例如,对于 MySQL 使用 DATETIME(6))。

+----------------+--------+-----------------------------+---------------------------+
| version        | phase  | started_at                  | finished_at               |
+----------------+--------+-----------------------------+---------------------------+
| 20220224045126 | before | 2023-02-17 05:19:50.225048 | 2023-02-17 05:19:50.672871 |
| 20220224045126 | after  | 2023-02-17 05:23:11.265727 | 2023-02-17 05:23:11.982982 |
+------------------------------------------------------+----------------------------+

与 doctrine/migrations 的区别

底层的差异检查和生成与doctrine/migrations中的操作相同,因为它使用了doctrine/dbal的特性。主要区别是我们不提供任何降级阶段。

这个库旨在提供在滚动更新部署中安全迁移所需的内核功能。基本上所有逻辑都在MigrationService中,它只有大约300行代码。我们试图使其尽可能轻量,不计划从doctrine/migrations复制功能。