- バッチバックグラウンドマイグレーションを使うタイミング
- バッチバックグラウンドマイグレーションの仕組み
- サンプル・プロジェクトの作成方法
- 管理
- EE専用機能のバックグラウンド一括マイグレーション
- デバッグ
- テスト
- ベストプラクティス
- 使用例
バックグラウンドでの一括マイグレーション
バッチバックグラウンドマイグレーションは、マイグレーションがガイドラインの制限時間を超える場合に、データマイグレーションを実行するために使用します。たとえば、1つのJSONカラムに格納されているデータを別のテーブルにマイグレーションする場合は、バッチバックグラウンドマイグレーションを使用します。
バッチバックグラウンドマイグレーションを使うタイミング
非常に多くの行を含むテーブルの_データを_マイグレーションする際に、通常のRailsマイグレーションを使用するとガイドラインの制限時間を超えてしまう場合に、バッチバックグラウンドマイグレーションを使用します。
- バッチのバックグラウンドマイグレーションは、トラフィックの多いテーブルのデータをマイグレーションするときに使用してください。
- バッチのバックグラウンドマイグレーションは、大規模なデータセットの各項目に対して多数の単一行クエリを実行する場合にも使用できます。通常、単一レコードパターンの場合、実行時間はデータセットのサイズに大きく依存します。それに応じてデータセットを分割し、バックグラウンドマイグレーションに入れます。
- スキーママイグレーションを実行するためにバッチバックグラウンドマイグレーションを使用しないでください。
バックグラウンドマイグレーションは次のような場合に役立ちます:
- イベントを1つのテーブルから複数のテーブルにマイグレーションする場合。
- 別のカラムに格納された JSON に基づいて、1 つのカラムにデータを入力します。
- 外部サービスの出力に依存するデータのマイグレーション。(たとえば API。)
備考
- バッチバックグラウンドマイグレーションが重要なアップグレードの一部である場合、リリースポストで発表する必要があります。マイグレーションがこのカテゴリーに該当するかどうか不明な場合は、プロジェクトマネージャーと相談してください。
- 必要なファイルがデフォルトで作成されるように、ジェネレーターを使用してバッチバックグラウンドマイグレーションを作成する必要があります。
バッチバックグラウンドマイグレーションの仕組み
バッチバックグラウンドマイグレーション(BBM) はGitlab::BackgroundMigration::BatchedMigrationJob
のサブクラスで、perform
メソッドを定義します。最初のステップとして、通常のマイグレーションは、batched_background_migrations
BBMクラスと必要な引数を持つレコードを batched_background_migrations
作成します。batched_background_migrations
デフォルトでは、 batched_background_migrations
マイグレーションはアクティビティ状態にあり、それらは実際のバッチマイグレーションを実行するためにSidekiqワーカーによってピックアップされます。
すべてのマイグレーションクラスは、名前空間Gitlab::BackgroundMigration
で定義する必要があります。ファイルをディレクトリlib/gitlab/background_migration/
に配置します。
実行メカニズム
バッチ処理されたバックグラウンドマイグレーションは、キューに入れられた順番にキューから取り出されます。複数のマイグレーションは、それらがアクティビティ状態にあり、同じデータベーステーブルをターゲットにしていない限り、フェッチされ、並列に実行されます。GitLab.comの場合、並行して処理されるマイグレーションのデフォルトの数は2つで、この制限は4つに設定されています。マイグレーションが実行用に選択されると、特定のバッチ用にジョブが作成されます。各ジョブの実行後、マイグレーションのバッチサイズは、直近の20ジョブのパフォーマンスに基づいて増減することができます。
ワーカーが利用可能になり次第、BBMはランナーによって処理されます。
べき等
バッチバックグラウンドマイグレーションは、Sidekiqプロセスのコンテキストで実行されます。通常のSidekiqルールが適用され、特にジョブは小さく、べき等であるというルールが適用されます。マイグレーションジョブが再試行される場合、データの整合性が保証されることを確認してください。
詳細については、Sidekiqベストプラクティスガイドラインを参照してください。
マイグレーション最適化
各ジョブの実行後、マイグレーションを最適化できるかどうかの検証が行われます。最適化の基礎となる仕組みは、時間効率の概念に基づいています。直近のNジョブの時間効率の指数移動平均を計算し、バッチバックグランドマイグレーションのバッチサイズを最適値に更新します。
ジョブ再試行メカニズム
バッチバックグラウンドマイグレーションのリトライメカニズムは、失敗した場合にジョブが再度実行されることを保証します。次の図は、リトライ機構のさまざまなステージを示しています:
-
MAX_ATTEMPTS
はGitlab::Database::BackgroundMigration
クラスで定義されています。 -
can_split?
はGitlab::Database::BatchedJob
クラスで定義されています。
失敗したバッチバックグラウンドマイグレーション
以下のいずれかが真である場合、バッチバックグラウンドマイグレーション全体はfailed
とマークされます (/chatops run batched_background_migrations status MIGRATION_ID
はfailed
としてマイグレーションを表示します ):
- 消費するジョブがなくなり、失敗したジョブがあるのです。
- バックグラウンドマイグレーションが開始されてから、半分以上のジョブが失敗しました。
バッチマイグレーションのスロットリング
バッチマイグレーションは更新負荷が高く、過去にはデータベースのパフォーマンスが低下している間にマイグレーションによる高負荷が原因で発生したインシデントがほとんどなかったため、それを軽減するためのスロットリング機構が存在します。
これらのデータベース指標はマイグレーションをスロットルするためにチェックされます。停止シグナルを受け取ると、マイグレーションは設定された時間(10分間)一時停止されます:
- アーカイブ保留中のWALキューがしきい値を超えました。
- マイグレーションが動作するテーブルのアクティブなオートバキューム。
- Patroni apdex SLIがSLOを下回っています。
- WALレートが閾値を超えました。
データベースの健全性チェック・フレームワークをさらに強化するために、より多くの指標を追加するための継続的な取り組みです。詳細はエピック7594を参照してください。
分離
バッチバックグラウンドマイグレーションは分離する必要があり、アプリケーションコード(たとえば、ApplicationRecord
クラス以外のapp/models
で定義されたモデル)を使用することはできません。これらのマイグレーションは実行に長い時間がかかるため、マイグレーションが実行されている間に新しいバージョンがデプロイされる可能性があります。
マイグレーションされたデータによって
通常のマイグレーションやポストマイグレーションとは異なり、次のリリースを待つだけでは、データが完全に移行されたことを保証することはできません。つまり、BBMが終了するまでデータに依存すべきではありません。100%移行されたデータが必要な場合は、ensure_batched_background_migration_is_finished
ヘルパーを使用して、マイグレーションが終了し、データが完全に移行されたことを保証することができます。(例を見てください)。
サンプル・プロジェクトの作成方法
バッチバックグランドマイグレーションを生成する方法
カスタムジェネレータbatched_background_migration
は必要なファイルを雛形化し、引数としてtable_name
,column_name
,feature_category
を受け取ります。使い方
bundle exec rails g batched_background_migration my_batched_migration --table_name=<table-name> --column_name=<column-name> --feature_category=<feature-category>
このコマンドは以下のファイルを作成します:
db/post_migrate/20230214231008_queue_my_batched_migration.rb
spec/migrations/20230214231008_queue_my_batched_migration_spec.rb
lib/gitlab/background_migration/my_batched_migration.rb
spec/lib/gitlab/background_migration/my_batched_migration_spec.rb
バッチバックグラウンドマイグレーションをエンキューします。
バッチ化されたバックグラウンドマイグレーションをキューに入れることは、デプロイ後のマイグレーションで行うべきです。マイグレーションがバッチで実行されるようにキューイングする例として、queue_batched_background_migration
。クラス名と引数をマイグレーションの値に置き換えてください:
queue_batched_background_migration(
JOB_CLASS_NAME,
TABLE_NAME,
JOB_ARGUMENTS,
JOB_INTERVAL
)
JOB_CLASS_NAME
で定義されたジョブ引数の数と一致しない場合、このヘルパーはエラーを発生させます。新しく作成されたデータがマイグレーションされるか、作成時に古いバージョンと新しいバージョンの両方で保存されることを確認してください。削除はカスケード削除の外部キーを定義することで処理できます。
ジョブ引数の使用
BatchedMigrationJob
はジョブクラスが必要とするジョブ引数を定義するためにjob_arguments
ヘルパーメソッドを提供します。
queue_batched_background_migration
でスケジュールされたバッチマイグレーションはジョブ引数を定義するためにヘルパーを使わなければなりません:
queue_batched_background_migration(
'CopyColumnUsingBackgroundMigrationJob',
TABLE_NAME,
'name', 'name_convert_to_text',
job_interval: DELAY_INTERVAL
)
queue_batched_background_migration
定義されたジョブ引数の数がマイグレーションをスケジューリングするときに提供されたジョブ引数の数と一致しない場合、queue_batched_background_migration
はエラーを発生します。
この例では、copy_from
はname
を返し、copy_to
はname_convert_to_text
を返します:
class CopyColumnUsingBackgroundMigrationJob < BatchedMigrationJob
job_arguments :copy_from, :copy_to
operation_name :update_all
def perform
from_column = connection.quote_column_name(copy_from)
to_column = connection.quote_column_name(copy_to)
assignment_clause = "#{to_column} = #{from_column}"
each_sub_batch do |relation|
relation.update_all(assignment_clause)
end
end
end
フィルタを使用
デフォルトでは、マイグレーションを実行するバックグラウンドジョブを作成する場合、バッチドバックグラウンドマイグレーションは指定されたテーブル全体を繰り返し処理します。この反復はPrimaryKeyBatchingStrategy
を使って行われます。 テーブルに1000レコードあり、バッチサイズが100の場合、ジョブは10回に分割されます。説明のために、EachBatch
はこのように使用されます:
# PrimaryKeyBatchingStrategy
Namespace.each_batch(of: 100) do |relation|
relation.where(type: nil).update_all(type: 'User') # this happens in each background job
end
場合によっては、レコードのサブセットのみを検査する必要があります。1000レコードのうち10%だけを検査する必要がある場合は、ジョブの作成時に最初のリレーションにフィルタを適用します:
Namespace.where(type: nil).each_batch(of: 100) do |relation|
relation.update_all(type: 'User')
end
最初の例では、各バッチで何件のレコードが更新されるかわかりません。最初の例では、各バッチで何件のレコードが更新されるかはわかりません。
BatchedMigrationJob
にはscope_to
ヘルパーメソッドが用意されており、 追加のフィルタを適用してこれを実現します:
-
BatchedMigrationJob
を継承し、追加フィルターを定義する新しいマイグレーションジョブクラスを作成します:class BackfillNamespaceType < BatchedMigrationJob scope_to ->(relation) { relation.where(type: nil) } operation_name :update_all feature_category :source_code_management def perform each_sub_batch do |sub_batch| sub_batch.update_all(type: 'User') end end end
scope_to
を定義する EE マイグレーションでは、モジュールがActiveSupport::Concern
を拡張していることを確認してください。そうしないと、スコープを考慮せずにレコードが処理されます。 -
デプロイ後のマイグレーションでは、バッチ化されたバックグラウンドマイグレーションをエンキューします:
class BackfillNamespaceType < Gitlab::Database::Migration[2.1] MIGRATION = 'BackfillNamespaceType' DELAY_INTERVAL = 2.minutes restrict_gitlab_migration gitlab_schema: :gitlab_main def up queue_batched_background_migration( MIGRATION, :namespaces, :id, job_interval: DELAY_INTERVAL ) end def down delete_batched_background_migration(MIGRATION, :namespaces, :id, []) end end
EachBatch
のパフォーマンスを最適化するために、それらがインデックスによって適切にカバーされていることを確認することが重要です。上の例では、フィルタをサポートするために(type, id)
にインデックスが必要です。詳細はEachBatch
のドキュメントを参照してください。複数のデータベースのデータにアクセス
バックグラウンドマイグレーションは、通常のマイグレーションとは異なり、複数のデータベースにアクセスすることができ、複数のデータベースにまたがって効率的にデータにアクセスし、更新することができます。使用するデータベースを適切に示すために、マイグレーションコードのインラインでActiveRecordモデルを作成することが望まれます。このようなモデルは、テーブルがどのデータベースにあるかに応じて、正しいApplicationRecord
。このように、ActiveRecord::Base
の使用は、指定されたテーブルにアクセスするために使用される明示的なデータベースを記述しないため、許可されません。
# good
class Gitlab::BackgroundMigration::ExtractIntegrationsUrl
class Project < ::ApplicationRecord
self.table_name = 'projects'
end
class Build < ::Ci::ApplicationRecord
self.table_name = 'ci_builds'
end
end
# bad
class Gitlab::BackgroundMigration::ExtractIntegrationsUrl
class Project < ActiveRecord::Base
self.table_name = 'projects'
end
class Build < ActiveRecord::Base
self.table_name = 'ci_builds'
end
end
同様に、ActiveRecord::Base.connection
の使用も認められませんので、できればモデル接続の使用と置き換える必要があります。
# good
Project.connection.execute("SELECT * FROM projects")
# acceptable
ApplicationRecord.connection.execute("SELECT * FROM projects")
# bad
ActiveRecord::Base.connection.execute("SELECT * FROM projects")
バックグラウンドマイグレーションを再キューイング
バッチ化されたバックグラウンドマイグレーションは、いくつかの理由で再実行する必要があるかもしれません:
- マイグレーションにバグが含まれている(例)。
- マイグレーションでデータをクリーンアップしましたが、アプリケーションロジックのバイパスにより、データが再び非正規化されました(例)。
- 元のマイグレーションのバッチサイズが原因でマイグレーションが失敗しました(例)。
バッチ化されたバックグラウンドマイグレーションを再実行するには、次の手順が必要です:
- 元のマイグレーションファイルの
#up
メソッドと#down
メソッドの内部をノーオープンにしてください。そうしないと、複数のパッチリリースを一度にアップグレードするシステムで、一括バックグラウンドマイグレーションが作成され、削除され、再び作成されます。 - バッチバックグラウンドマイグレーションを再実行する新しいポストデプロイマイグレーションを追加します。
- 新しいデプロイ後マイグレーションでは、
#up
メソッドの開始時にdelete_batched_background_migration
メソッドを使用して既存のバッチバックグラウンドマイグレーションを削除し、既存の実行がクリーンアップされるようにします。 - 元のマイグレーションから
db/docs/batched_background_migration/*.yml
ファイルを更新して、リクエキューに関する情報を含めます。
非分離カラムに対するバッチ処理
デフォルトのバッチ処理方式は、主キーカラムを効率的に繰り返し処理するためのものです。しかし、値が一意でないカラムを繰り返し処理する必要がある場合は、 別のバッチ処理を使用しなければなりません。
LooseIndexScanBatchingStrategy
バッチング戦略では、EachBatch
の特別なバージョンを使用して、一意でないカラム値に対する効率的で安定した反復処理を行います。
この例では、issues.project_id
列がバッチ列として使用されるバッチバックグランドマイグレーションを示しています。
マイグレーション後のデータベース:
class ProjectsWithIssuesMigration < Gitlab::Database::Migration[2.1]
MIGRATION = 'BatchProjectsWithIssues'
INTERVAL = 2.minutes
BATCH_SIZE = 5000
SUB_BATCH_SIZE = 500
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
def up
queue_batched_background_migration(
MIGRATION,
:issues,
:project_id,
job_interval: INTERVAL,
batch_size: BATCH_SIZE,
batch_class_name: 'LooseIndexScanBatchingStrategy', # Override the default batching strategy
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
delete_batched_background_migration(MIGRATION, :issues, :project_id, [])
end
end
バックグラウンドマイグレーションクラスの実装:
module Gitlab
module BackgroundMigration
class BatchProjectsWithIssues < Gitlab::BackgroundMigration::BatchedMigrationJob
include Gitlab::Database::DynamicModelHelpers
operation_name :backfill_issues
def perform
distinct_each_batch do |batch|
project_ids = batch.pluck(batch_column)
# do something with the distinct project_ids
end
end
end
end
end
バッチバックグランドマイグレーションの全体的な時間推定の計算
BBMが完了するまでの時間を見積もることができます。GitLab はすでにパイプラインを通して見積もりを提供していますdb:gitlabcom-database-testing
。この見積もりはテスト環境での本番データのサンプリングに基づいて構築されており、マイグレーションにかかる可能性のある最大時間を表しています。シナリオによっては db:gitlabcom-database-testing
、パイプラインがdb:gitlabcom-database-testing
提供する見積もりだけ db:gitlabcom-database-testing
では、マイグレーションされるレコード周辺のすべての特異点を計算できず、さらなる計算が必要になることがあります。このような場合、interval * number of records / max batch size
の式を使用して、マイグレーションにかかる時間の概算を求めることができます。ここで、interval
とmax batch size
はジョブに対して定義されたオプションを指し、total tuple count
はマイグレーションされるレコードの数です。
バッチバックグランドマイグレーションのクリーンアップ
バックグラウンドマイグレーションには長い時間がかかることがあるため、キューに入れた後すぐに片付けることはできません。例えば、マイグレーションプロセスで使用されたカラムを削除することはできません。将来のリリースで_デプロイ後の_マイグレーションを別途追加し、残りのジョブを完了させてから片付ける必要があります。(例えば、カラムを削除するなど)。
カラムfoo
(大きなJSONブロブを含む)からカラムbar
(文字列を含む)にデータをマイグレーションするには、次のようにします:
- リリースA
- 指定された ID を持つ行のマイグレーションを実行するマイグレーション・クラスを作成します。
- これらの手法のいずれかを使用して、新しい行を更新します:
- アプリケーションロジックを必要としないコピーオペレーション用の新しいトリガを作成します。
- このオペレーションは、レコードが作成または更新されるときにモデル/サービスで処理します。
- レコードを更新する新しいカスタムバックグラウンドジョブを作成します。
- デプロイ後のマイグレーションで、既存のすべての行のバッチバックグラウンドマイグレーションをキューに入れます。
- リリースB
- バッチバックグラウンドマイグレーションが完了したかどうかをチェックするデプロイ後のマイグレーションを追加しました。
- アプリケーションが新しいカラムの使用を開始し、新しいレコードの更新を停止するようにコードをデプロイします。
- 古いカラムを削除します。
以前のバージョンのGitLabからプロジェクトをインポートする際に、データを新しいフォーマットにする必要がある場合は、インポート/エクスポートのバージョンを上げる必要があるかもしれません。
バッチバックグランドマイグレーションをサポートするインデックスの追加
バッチ化されたバックグラウンドマイグレーションをサポートするために、新しいインデックスや一時的なインデックスを追加する必要がある場合があります。これを行うには、バックグラウンドマイグレーションをキューに入れるデプロイ後のマイグレーションより前に、デプロイ後のマイグレーションでインデックスを作成します。
作成後にインデックスを直接使用できるようにするために特別な注意を必要とするいくつかのケースに関する追加情報については、データベースインデックスの追加に関するドキュメントを参照してください。
データベースのテストパイプラインで特定のバッチを実行します。
GitLab.com の特定のバッチでバックグラウンドマイグレーションが失敗し、どのクエリがなぜ失敗したのかを調べたいとします。今のところ、クエリ情報 (特にクエリパラメータ) を取得する良い方法はありません。また、マイグレーション全体を再実行し、さらにロギングを行うには長い時間がかかります。
幸いなことに、私たちのデータベースマイグレーションパイプラインを活用することで、特定のバッチをログを追加したり修正したりして再実行し、問題が解決するかどうかを確認することができます。
例として、Draft:Test PG::CardinalityViolation
fix を参照してください。
そのためには、以下のことが必要です:
バッチstart_id
を見つけend_id
Kibana でこれらを見つけることができるはずです。
通常のマイグレーションの作成
定期マイグレーションのup
ブロックでバッチをスケジュールします:
def up
instance = Gitlab::BackgroundMigration::YourBackgroundMigrationClass.new(
start_id: <batch start_id>,
end_id: <batch end_id>,
batch_table: <table name>,
batch_column: <batching column>,
sub_batch_size: <sub batch size>,
pause_ms: <miliseconds between batches>,
job_arguments: <job arguments if any>,
connection: connection
)
instance.perform
end
def down
# no-op
end
マイグレーションヘルパーの回避策を適用する(オプション)
バッチ化されたバックグラウンドマイグレーションで、restrict_gitlab_migration
ヘルパーを使用して指定したスキーマ以外のスキーマのテーブルに触れた場合 (例: スケジューリングマイグレーションではrestrict_gitlab_migration gitlab_schema: :gitlab_main
を使用しているが、バックグラウンドジョブでは:gitlab_ci
スキーマのテーブルを使用している)、マイグレーションは失敗します。これを防ぐには、テストパイプラインジョブを失敗させないようにデータベースヘルパーを修正する必要があります:
- スキーマ名を
RestrictGitlabSchema
diff --git a/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb b/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
index b8d1d21a0d2d2a23d9e8c8a0a17db98ed1ed40b7..912e20659a6919f771045178c66828563cb5a4a1 100644
--- a/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
+++ b/lib/gitlab/database/migration_helpers/restrict_gitlab_schema.rb
@@ -55,7 +55,7 @@ def unmatched_schemas
end
def allowed_schemas_for_connection
- Gitlab::Database.gitlab_schemas_for_connection(connection)
+ Gitlab::Database.gitlab_schemas_for_connection(connection) << :gitlab_ci
end
end
end
- スキーマ名を
RestrictAllowedSchemas
diff --git a/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb b/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
index 4ae3622479f0800c0553959e132143ec9051898e..d556ec7f55adae9d46a56665ce02de782cb09f2d 100644
--- a/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
+++ b/lib/gitlab/database/query_analyzers/restrict_allowed_schemas.rb
@@ -79,7 +79,7 @@ def restrict_to_dml_only(parsed)
tables = self.dml_tables(parsed)
schemas = self.dml_schemas(tables)
- if (schemas - self.allowed_gitlab_schemas).any?
+ if (schemas - (self.allowed_gitlab_schemas << :gitlab_ci)).any?
raise DMLAccessDeniedError, \
"Select/DML queries (SELECT/UPDATE/DELETE) do access '#{tables}' (#{schemas.to_a}) " \
"which is outside of list of allowed schemas: '#{self.allowed_gitlab_schemas}'. " \
データベースマイグレーションパイプラインの開始
変更内容でマージリクエストを作成し、手動db:gitlabcom-database-testing
ジョブを起動します。
管理
chatops
インテグレーションを通して行われ、GitLabチームメンバーのみに限定されています。バッチバックグランドマイグレーション一覧
システム内のバッチ化されたバックグラウンドマイグレーションを一覧表示するには、このコマンドを実行します:
/chatops run batched_background_migrations list
このコマンドは以下のオプションをサポートしています:
- データベースの選択:
-
--database DATABASE_NAME
:指定されたデータベースに接続します:-
main
:メインデータベースを使用します(デフォルト)。 -
ci
:CI データベースを使用します。
-
-
- 環境選択:
-
--dev
:dev
環境を使用します。 -
--staging
:staging
環境を使用します。 -
--staging_ref
:staging_ref
環境を使用します。 -
--production
production
環境を使用します (デフォルト)。
-
出力例:
created_at
(DESC)の順番で20のバッチバックグランドマイグレーションを返します。バッチバックグラウンドマイグレーションの進行状況とステータスの監視
特定のバッチバックグラウンドマイグレーションのステータスと進捗状況を確認するには、このコマンドを実行します:
/chatops run batched_background_migrations status MIGRATION_ID
このコマンドは以下のオプションをサポートしています:
- データベースの選択:
-
--database DATABASE_NAME
:指定されたデータベースに接続します:-
main
:メインデータベースを使用 (デフォルト) -
ci
:CIデータベースを使用
-
-
- 環境選択:
-
--dev
:dev
環境を使用します。 -
--staging
:staging
環境を使用します。 -
--staging_ref
:staging_ref
環境を使用します。 -
--production
production
環境を使用します (デフォルト)。
-
出力例:
Progress
はバックグラウンドマイグレーションが完了した割合を表します。
バッチバックグラウンドマイグレーション状態の定義:
-
アクティビティ:どちらか:
- ランナーに選ばれる準備ができています。
- バッチジョブの実行。
- ファイナライズバッチジョブの実行。
- 失敗しました:バッチバックグラウンドマイグレーションに失敗しました。
- 終了しました:バッチバックグラウンドマイグレーションが完了しました。
- 一時停止:ランナーには表示されません。
バッチバックグラウンドマイグレーションを一時停止します。
バッチバックグラウンドマイグレーションを一時停止したい場合は、以下のコマンドを実行する必要があります:
/chatops run batched_background_migrations pause MIGRATION_ID
このコマンドは以下のオプションをサポートしています:
- データベースの選択:
-
--database DATABASE_NAME
:指定されたデータベースに接続します:-
main
:メインデータベースを使用します(デフォルト)。 -
ci
:CI データベースを使用します。
-
-
- 環境選択:
-
--dev
:dev
環境を使用します。 -
--staging
:staging
環境を使用します。 -
--staging_ref
:staging_ref
環境を使用します。 -
--production
production
環境を使用します (デフォルト)。
-
出力例:
active
バッチバックグラウンドマイグレーションのみを一時停止できます。バッチバックグランドマイグレーション再開
バッチバックグラウンドマイグレーションを再開したい場合は、以下のコマンドを実行する必要があります:
/chatops run batched_background_migrations resume MIGRATION_ID
このコマンドは以下のオプションをサポートしています:
- データベースの選択:
-
--database DATABASE_NAME
:指定されたデータベースに接続します:-
main
:メインデータベースを使用します(デフォルト)。 -
ci
:CI データベースを使用します。
-
-
- 環境選択:
-
--dev
:dev
環境を使用します。 -
--staging
:staging
環境を使用します。 -
--staging_ref
:staging_ref
環境を使用します。 -
--production
production
環境を使用します (デフォルト)。
-
出力例:
active
バッチバックグラウンドマイグレーションのみ再開できます。バックグラウンドマイグレーションを有効または無効にします。
極めて限定された状況において、GitLab管理者はこれらの機能フラグのどちらか、あるいは両方を無効にすることができます:
execute_background_migrations
execute_batched_migrations_on_schedule
これらのフラグはデフォルトで有効になっています。これらのフラグを無効にするのは、データベースホストのメンテナンスのような特別な状況でデータベースのオペレーションを制限するための最後の手段としてだけにしてください。
execute_background_migrations
またはexecute_batched_migrations_on_schedule
機能フラグを無効にすると、GitLabのアップグレードが失敗し、データの損失が発生する可能性があります。EE専用機能のバックグラウンド一括マイグレーション
EE専用機能のためのバックグラウンドマイグレーションクラスは全てGitLab FOSSに存在する必要があります。この目的のために、GitLab FOSS用に空のクラスを作成し、Enterprise Editionの機能を実装するためのガイドラインで説明されているように、GitLab EE用に拡張します。
新しいバッチドバックグラウンドマイグレーションを生成するときに--ee-only
フラグを渡すことで、ジェネレータを使ってEE-only のマイグレーション雛形を生成することができます。
デバッグ
エラーログの表示
失敗を表示するには 2 つの方法があります:
- GitLab ログから:
-
バッチ化されたバックグラウンドマイグレーションを実行した後、いずれかのジョブが失敗した場合、Kibanaでログを表示します。本番Sidekiqログを表示し、フィルタリングします:
json.new_state: failed
json.job_class_name: <Batched Background Migration job class name>
json.job_arguments: <Batched Background Migration job class arguments>
-
json.exception_class
とjson.exception_message
の値をレビューして、ジョブが失敗した理由を理解します。 -
リトライの仕組みを覚えておいてください。失敗したからといって、ジョブが失敗したわけではありません。常にジョブの最後のステータスをチェックしてください。
-
-
データベース経由で:
- バッチバックグランドマイグレーションを取得
CLASS_NAME
. -
PostgreSQLコンソールで以下のクエリを実行します:
SELECT migration.id, migration.job_class_name, transition_logs.exception_class, transition_logs.exception_message FROM batched_background_migrations as migration INNER JOIN batched_background_migration_jobs as jobs ON jobs.batched_background_migration_id = migration.id INNER JOIN batched_background_migration_job_transition_logs as transition_logs ON transition_logs.batched_background_migration_job_id = jobs.id WHERE transition_logs.next_status = '2' AND migration.job_class_name = "CLASS_NAME";
- バッチバックグランドマイグレーションを取得
テスト
テストの作成は、次のような場合に必要です:
- バッチバックグランドマイグレーションのキューイングマイグレーション。
- バッチバックグランドマイグレーションそのもの。
- クリーンアップマイグレーション。
:migration
およびschema: :latest
RSpec タグはバックグラウンドマイグレーション仕様に自動的に設定されます。Testing Railsマイグレーションスタイルガイドを参照してください。
before
およびafter
RSpec フックがデータベースをダウンマイグレーションおよびアップマイグレーションすることを覚えておいてください。これらのフックを使用すると、他のバッチバックグラウンドマイグレーションが呼び出される可能性があります。spy
have_received
it
ブロックで定義した期待値が、RSpec フックで呼び出されるものと衝突する可能性があるからです。詳細はイシュー#35351を参照してください。
ベストプラクティス
- 扱うデータの量を把握しましょう。
- バッチ化されたバックグラウンドマイグレーションジョブが冪等であることを確認してください。
- あなたが書いたテストが偽陽性でないことを確認してください。
- マイグレーションされるデータがクリティカルで失うことができない場合、クリーンアップマイグレーションは完了する前にデータの最終状態もチェックする必要があります。
- データベースのスペシャリストと数字について話し合ってください。マイグレーションは、予想以上にDBに負担をかける可能性があります。ステージングで測定するか、本番環境で測定するよう誰かに依頼してください。
- バッチバックグランドマイグレーションを実行するのに必要な時間を把握してください。
-
ジョブクラスの内部で例外をサイレントレスキューする場合は注意してください。これは、失敗シナリオであっても、ジョブが成功としてマークされる可能性があります。
# good def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') end end # acceptable def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') rescue Exception => error logger.error(message: error.message, class: error.class) raise end end # bad def perform each_sub_batch do |sub_batch| sub_batch.update_all(name: 'My Name') rescue Exception => error logger.error(message: error.message, class: self.class.name) end end
使用例
ルートの使用例
routes
テーブルにはsource_type
フィールドがあり、多相リレーションシップに使用されています。データベースの再設計の一環として、多相リレーションシップを削除します。その作業の一段階として、source_id
列のデータを新しい単数の外部キーにマイグレーションします。古い行は後で削除する予定なので、バックグラウンドマイグレーションの一部として更新する必要はありません。
-
ジェネレータを使用して、バックグラウンドマイグレーションファイルをバッチで作成することから始めます:
bundle exec rails g batched_background_migration BackfillRouteNamespaceId --table_name=routes --column_name=id --feature_category=source_code_management
-
マイグレーションジョブ(
BatchedMigrationJob
のサブクラス)を更新して、source_id
の値をnamespace_id
にコピーします:class Gitlab::BackgroundMigration::BackfillRouteNamespaceId < BatchedMigrationJob # For illustration purposes, if we were to use a local model we could # define it like below, using an `ApplicationRecord` as the base class # class Route < ::ApplicationRecord # self.table_name = 'routes' # end operation_name :update_all feature_category :source_code_management def perform each_sub_batch( batching_scope: -> (relation) { relation.where("source_type <> 'UnusedType'") } ) do |sub_batch| sub_batch.update_all('namespace_id = source_id') end end end
ジョブクラスはBatchedMigrationJob
バッチマイグレーションフレームワークによって正しく処理さBatchedMigrationJob
れるように継承します。BatchedMigrationJob
のサブクラスはBatchedMigrationJob
バッチを実行するために必要な引数とトラッキングデータベースへの接続で初期化されます。 -
データベースに新しいトリガを追加するデータベースマイグレーションを作成します。例
class AddTriggerToRoutesToCopySourceIdToNamespaceId < Gitlab::Database::Migration[2.1] FUNCTION_NAME = 'example_function' TRIGGER_NAME = 'example_trigger' def up execute(<<~SQL) CREATE OR REPLACE FUNCTION #{FUNCTION_NAME}() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW."namespace_id" = NEW."source_id" RETURN NEW; END; $$; CREATE TRIGGER #{TRIGGER_NAME}() AFTER INSERT OR UPDATE ON routes FOR EACH ROW EXECUTE FUNCTION #{FUNCTION_NAME}(); SQL end def down drop_trigger(TRIGGER_NAME, :routes) drop_function(FUNCTION_NAME) end end
-
作成したデプロイ後のマイグレーションを、必要な遅延とバッチサイズで更新します:
class QueueBackfillRoutesNamespaceId < Gitlab::Database::Migration[2.1] MIGRATION = 'BackfillRouteNamespaceId' DELAY_INTERVAL = 2.minutes BATCH_SIZE = 1000 SUB_BATCH_SIZE = 100 restrict_gitlab_migration gitlab_schema: :gitlab_main def up queue_batched_background_migration( MIGRATION, :routes, :id, job_interval: DELAY_INTERVAL, batch_size: BATCH_SIZE, sub_batch_size: SUB_BATCH_SIZE ) end def down delete_batched_background_migration(MIGRATION, :routes, :id, []) end end
バッチバックグラウンドマイグレーションをキューイングする場合、スキーマを実際に変更を行うデータベースに制限する必要があります。この場合、routes
レコードを更新するので、restrict_gitlab_migration gitlab_schema: :gitlab_main
を設定します。しかし、CIデータマイグレーションを実行する必要がある場合は、restrict_gitlab_migration gitlab_schema: :gitlab_ci
を設定します。デプロイ後、私たちのアプリケーション: - 以前と同様にデータを使用します。 - 既存データと新規データの両方を確実にマイグレーション。
-
バッチ化されたバックグラウンドマイグレーションが完了したことを確認する新しいデプロイ後マイグレーションを追加します。例えば
class FinalizeBackfillRouteNamespaceId < Gitlab::Database::Migration[2.1] MIGRATION = 'BackfillRouteNamespaceId' disable_ddl_transaction! restrict_gitlab_migration gitlab_schema: :gitlab_main def up ensure_batched_background_migration_is_finished( job_class_name: MIGRATION, table_name: :routes, column_name: :id, job_arguments: [], finalize: true ) end def down # no-op end end
バッチ化されたバックグラウンドマイグレーションが終了していない場合、システムはバッチ化されたバックグラウンドマイグレーションをインラインで実行します。この動作を見たくない場合は、finalize: false
.アプリケーションが100%マイグレーションされるデータに依存していない場合(例えば、データはアドバイザリーであり、ミッションクリティカルではない)、この最終ステップはスキップできます。このステップでは、マイグレーションが完了し、すべての行がマイグレーションされたことを確認します。
-
データベースマイグレーションを追加して、トリガーを削除します。
class RemoveNamepaceIdTriggerFromRoutes < Gitlab::Database::Migration[2.1] FUNCTION_NAME = 'example_function' TRIGGER_NAME = 'example_trigger' def up drop_trigger(TRIGGER_NAME, :routes) drop_function(FUNCTION_NAME) end def down # Should reverse the trigger and the function in the up method of the migration that added it end end
バッチマイグレーションが完了した後、routes.namespace_id
のデータに安心して依存することができます。