コンテンツへスキップ

サイト名を変えてもデータが消えない設計 — 表示名と安定識別子を分けた話

クライアントから「サイト名を『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-stagingacme になると、新しいディレクトリ 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% 信用してファイル所在を決める設計は、リネームの自由度を初日から閉ざしてしまう、というのが今回の振り返りです。