Appearance
機種マスタ移行ロードマップ(Issue #2135)
親 Issue: #2135 機種名の文字列保持をマスタID参照に改修する
背景
現状、機種は各テーブルの kishu 列に文字列で保持され、表示用への変換は db_kishu_display_mapping と「登録データに反映」による一括 UPDATE で同期している。機種名リネーム後に参照元だけ古い名称が残る、反映対象テーブルの都度追加、表記ゆれによる集計複雑化が課題となる。
フェーズ一覧
| フェーズ | 内容 | 実装単位 |
|---|---|---|
| 1 | マスタテーブル db_kishu_master の設計・Installer・DBML・ドキュメント整備 | 本 PR(KishuMasterInstaller) |
| 2 | 既存 kishu 文字列のマスタへの初期投入、インポート時の未登録機種の自動マスタ登録 | #2137 |
| 3 | 各業務テーブルへ kishu_id 列追加・バックフィル(文字列と並行保持) | #2138 |
| 4 | 参照を kishu_id に切り替え、db_kishu_display_mapping の整理(db2023.kishu は確認用に永続保持) | #2139 |
移行対象テーブル(文字列 kishu 保持)
| テーブル(suffix) | 現状のキー列 | フェーズ3以降 |
|---|---|---|
db2023 | kishu | kishu_id 追加。kishu は raw 名称確認列として永続保持(DROP しない) |
db_daily_article_kishu_single_day_summary | kishu | kishu_id 追加 |
db_daily_article_kishu_count_delta | kishu | kishu_id 追加 |
db_birthday_kishu | kishu | kishu_id 追加 |
db_kishu_display_mapping | data_kishu / display_kishu | マスタ・別名テーブルへの取り込み検討(フェーズ4) |
KishuIndex | Name | 別途優先度整理 |
移行期間中の二重管理方針(フェーズ3)
- 保持: 既存の文字列列(
kishu)と新規kishu_idを並行して保持する。 - 書き込み: 新規・更新時は
kishu_idと文字列の両方を書き込む(バックフィル完了後もフェーズ3中は維持)。 - 読み取り: フェーズ3中は表示・集計の参照は従来どおり文字列
kishuを優先する。kishu_idが NULL の行はバックフィル対象として扱う。 - 検証: バックフィル後、同一行で
kishuとマスタnameの突合が一致することを確認してからフェーズ4へ進む。
フェーズ4完了後の想定
- 業務テーブルは
kishu_idで機種を参照し、表示名はマスタ(または別名テーブル)から解決する。 db2023.kishuは DROP しない。インポート元 raw 名称(マッピング変換前)を確認用データとして永続保持する。アプリケーションからは参照しない(PR #2285)。- summary / delta / birthday_kishu 等の他業務テーブルについては、フェーズ4で
kishu文字列列の廃止を検討する(db2023は対象外)。 db_kishu_display_mappingの「登録データに反映」一括 UPDATE は不要になる方向で整理する(#2134 系の同期対象拡張はマスタ移行完了後に見直し)。
非スコープ(#2135 全体)
- #2134 の
db_birthday_kishuへの「登録データに反映」拡張(別 PR 可) - マスタ移行完了前の既存マッピング UI の即時削除
関連 Issue
- #1203(機種表示マッピング)
- #2076(機種別サマリ)
- #2134(登録データ反映で summary / delta を同期)
マイグレーション手順(フェーズ1)
- 管理画面初回読み込み時(
admin_init)にAdminDatabaseInstaller::ensure_all()が実行され、KishuMasterInstaller::install()が走る。 VersionedTableInstallerTraitにより、db_kishu_masterが未作成、またはインストール済みバージョンが current 未満、または必須カラムの不足・禁止カラムの存在など現在の実装で検証している範囲で不一致がある場合にdbDeltaが実行され、必要な作成・更新を行ったうえでオプションkishu_master_db_versionを1に更新する。- 既にテーブルが存在し、必要なカラム構成が満たされており、かつバージョン更新も不要な場合は即 return(冪等)。
マイグレーション手順(フェーズ3 / Issue #2138)
前提: フェーズ2(db_kishu_master へのシード・インポート時自動登録)が完了していること。
- 列追加(Installer):
initで次が冪等実行される。DailyArticleKishuSummaryInstaller(v4)DailyArticleKishuCountDeltaInstaller(v2)DailyDataKishuIdInstaller(db2023にkishu_idのみ追加)BirthdayKishuInstaller(db_birthday_kishu)
- バックフィル: 格納済みの文字列
kishuとdb_kishu_master.nameを突合しkishu_idを UPDATE。未登録名は先にupsert_by_namesでマスタへ投入。- 事前(
db_birthday_kishuのみ):wp slot-kouryaku kishu-master-seed run—db_birthday_kishu.kishuの DISTINCT 名称をdb_kishu_masterへ INSERT IGNORE(Issue #2290)。kishu-id-backfill run実行前に推奨。 - 小テーブル(summary / delta / birthday_kishu):
wp slot-kouryaku kishu-id-backfill run(テーブル指定なしで secondary のみ) db2023:wp slot-kouryaku kishu-id-backfill run --table=daily_data --batch-size=5000でバッチ繰り返し
- 事前(
- 検証:
wp slot-kouryaku kishu-id-backfill verifyでkishu_id IS NULL残件数を確認。0 件が理想。db_birthday_kishuのマスタ未登録名は次で確認できる(0 件が理想):sqlSELECT COUNT(DISTINCT TRIM(bk.kishu)) FROM wp_db_birthday_kishu bk LEFT JOIN wp_db_kishu_master m ON TRIM(bk.kishu) = m.name AND m.is_active = 1 WHERE TRIM(bk.kishu) != '' AND m.id IS NULL; - 以降の書き込み: インポート・サマリ再計算・誕生日 CRUD・マッピング反映で
kishu_idを書き込む。db2023には raw 名称をkishu列にも保存(確認用)。読み取りはkishu_idJOIN マスタを優先。
マイグレーション手順(フェーズ4 / Issue #2139)
正本ドキュメント: 本ファイル。Issue #2139 本文のロードマップと同期すること。
前提: フェーズ3(4業務テーブルへの kishu_id 列追加・バックフィル CLI)が完了していること。PR #2273(db_kishu_display_mapping の kishu_id FK 化)マージ済み。
staging / 本番共通の注意(collation)
- 既存
db_kishu_display_mapping(2023年作成)はutf8mb4_unicode_ciのことが多い。 - 新規
db_kishu_masterは$wpdb->get_charset_collate()由来(WP 6.x ではutf8mb4_unicode_520_ci等)になりやすい。 display_kishu = master.nameの列同士 JOIN は MySQL #1267(collation 混在)で失敗し、kishu_idバックフィルが進まない。- 対応: PR #2279 以降、
KishuDisplayMappingInstallerのバックフィル JOIN にCOLLATEを付与。
「機種マスタへ投入」の件数について
- マッピング 行数(例: 439)と「処理 N 件」は一致しない場合がある。
- 「処理 N 件」=
display_kishuの 正規化後ユニーク件数(複数data_kishu→ 同一display_kishuは1件に集約)。 - 「新規 M 件」= マスタに未登録だった名称のみ
INSERT IGNORE。
本番一括デプロイ推奨順(フェーズ1〜4 を一度に入れる場合)
- コードデプロイ + PHP-DI キャッシュ削除(
core_src/config/di.php変更時) - 管理画面を1回開く —
db_kishu_masterテーブル作成(KishuMasterInstaller) - 機種表示マッピング管理画面で「機種マスタへ投入」 —
display_kishuをdb_kishu_masterに upsert(バックフィル前提。自動ではない) - 未マッチ確認(SQL) —
display_kishuとmaster.nameが突合できること(COLLATE 付き JOIN で 0 件が理想) kishu_display_mapping_backfill_attempts確認 — 5 到達時はwp option delete kishu_display_mapping_backfill_attempts後に再試行- 管理画面または任意ページを再読み込み —
KishuDisplayMappingInstallerでkishu_idバックフィル +display_kishuDROP(kishu_display_mapping_db_version = 2) - 4業務テーブルの
kishu_idバックフィル —wp slot-kouryaku kishu-id-backfill run(#2138)。db_birthday_kishuについては事前にwp slot-kouryaku kishu-master-seed run(#2290)を推奨。 - 動作確認 — 日別インポート・マッピング保存同期・ランキング表示等
デプロイ直後の注意: 手順3完了前に一般公開すると、init でバックフィル失敗が backfill_attempts に蓄積され得る。可能なら手順2〜6を連続実施する。
Installer 実行タイミング(参考)
| Installer | トリガー |
|---|---|
KishuDisplayMappingInstaller(バックフィル含む) | init priority 0 + admin_init |
KishuMasterInstaller(テーブル作成のみ) | admin_init のみ |
| マスタデータ投入 | 管理画面「機種マスタへ投入」(手動) |
staging でバックフィルが止まったときの手動復旧(コード修正前)
sql
UPDATE wp_db_kishu_display_mapping AS m
INNER JOIN wp_db_kishu_master AS km
ON km.name = m.display_kishu COLLATE utf8mb4_unicode_520_ci
AND km.is_active = 1
SET m.kishu_id = km.id
WHERE (m.kishu_id IS NULL OR m.kishu_id = 0)
AND m.display_kishu IS NOT NULL AND m.display_kishu != '';その後 wp option delete kishu_display_mapping_backfill_attempts → ページ再読み込みで display_kishu DROP を Installer に任せる。