クライアントから「サイト名を『acme-staging』から本番の『acme』に変えてほしい」と言われた。アプリ上でサイト名をリネームした瞬間、それまで取っていた DB バックアップ・スクリーンショット・サムネイルが全部消えたように見える。
実際にはファイルは残っているけれど、新しい名前のディレクトリには中身がない。「同じサイト」として継承されていない。これは Day 1 から踏みうる落とし穴で、私たちも当初の設計でやらかしていた問題でした。
今回は、この「リネームでデータが孤立する」現象をどう設計で塞いだか、という話です。
なぜ消えるのか — サイト名がキーになっていた
当初の WP メンテナンスマネージャは、ファイル所在を サイト名で決める設計でした。
backups/
acme-staging/ ← サイト名 "acme-staging" の DB バックアップ
backup_20260101_120000.sql
screenshots/
acme-staging/ ← 同じく "acme-staging" のスクショ
home_pre.png
リネームで acme-staging → acme になると、新しいディレクトリ backups/acme/ が作られて空のまま動き始める。旧ディレクトリは残るけれど、アプリからは「別サイトの古いデータ」として扱われ、UI には現れない。
サイト名は表示・識別のためのラベルとして自然な選択肢ですが、運用上は頻繁にリネームされうるものでした。クライアント名の表記揺れ修正、ステージング → 本番への昇格、組織変更による命名規則変更 — リネームの動機は無数にあります。
解決策 — 不変な site_id を全サイトに付与
各サイトに site_xxxxxxxxxxxx 形式の UUID(12 hex)を _id として付与し、ファイル所在もすべて _id ベースに移行しました。
# core/site_id_utils.py
def generate_site_id():
return f"site_{uuid.uuid4().hex[:12]}"
_id は一度付与されたら絶対に変わらない。サイト名がリネームされても、ファイル所在は _id で決まるので、backups/site_a1b2c3d4e5f6/ という不変ディレクトリの中身を継続的に使い続けます。
UI 上の表示名(サイト名)と内部識別子(_id)を分離した、よくある二層構造の設計です。
既存データを壊さない冪等な migration
最大の難所は、既にサイト名キーで動いていた既存ユーザーのデータをどう引き継ぐかでした。
実装した ensure_site_ids() は冪等な migration:
_idが未付与のサイトにだけ自動で生成・付与- 既に
_idがついているサイトは触らない FileLock+ tempfile +os.replace()の atomic write で書き込み途中のクラッシュでも壊れない
アプリ起動時とサイト関連 API 呼び出し時の 3 経路で実行され、ユーザーは何も操作する必要がない。バックグラウンドで静かに ID が付与される。
ファイル側の引っ越しも同じ思想で、初回起動時に backups/<site_name>/ を発見したら backups/<site_id>/ にリネームする(ただし既に新形式が存在する場合は触らない)冪等処理にしました。
ログとの紐付け — 厳密一致+互換マッチのハイブリッド
ログエントリにも site_id を埋め込みましたが、既存ログには site_id が無い(リネーム前に取られたログ)。
UI のスコープ機能(特定サイトのログだけ表示)は次のハイブリッドで実装:
- 新しいログ(
site_id付き)→ 厳密一致で絞り込む - 旧ログ(
site_idなし)→site_name互換マッチで拾う
これでリネーム前後のログが同じスコープに混在表示される。「過去のログが消えた」と感じさせない継承。
リリース後にやらかしたバグ
副次の話として、リリース直後に踏んだバグも記録しておきます。is_valid_site_id というバリデーション関数で新規生成形式にしかマッチしない regex を書いていて、過去に生成された一部の長めの _id を全部拒否していました。
# やらかし版
SITE_ID_RE = re.compile(r'^site_[0-9a-f]{12}$') # 12 hex 固定
移行期の試行錯誤で生まれた長めの ID 形式が履歴に残っていて、これらが全部はじかれて「サイトが全部消えた」状態に。既存データの形式を完全に把握してから validation を厳格化する、という当たり前のことを後付け validation でやらかしたケースでした。
まとめ — 安定識別子と表示名の分離
「表示用の名前」と「内部の識別子」を分けるのは古典的なソフトウェア設計パターンですが、運用に乗ったあとで導入するのは設計コストが跳ね上がります。既存データを壊さない冪等な migration、編集と複製の責務分担、validation の後方互換性 — どれか 1 つを落とすと既存ユーザーのデータが消える。
サイト名(表示用)と site_id(不変)を分離して以降、クライアント名の表記揺れ修正・ステージング → 本番移行・組織再編に伴うリネーム、どれも過去のデータを保持したまま自由に名前を変えられる運用に変わりました。表示名を 100% 信用してファイル所在を決める設計は、リネームの自由度を初日から閉ざしてしまう、というのが今回の振り返りです。