0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

current/backup/quarantineでセーブを守る【第35回】

0
Last updated at Posted at 2026-09-14

4Hえんぴつ|React+TypeScriptでWeb RPGを作る
第7章「テストと品質管理」
第35回:current/backup/quarantineでセーブを守る

連載トップ・第0回はこちら

1. 今回のテーマ

前回は、

セーブデータを読み込んだときに、

valid
migratable
unsupported
invalid

へ分類する考え方を整理しました。

今回は、

その中でも、

invalid

と判断されたセーブを、

どう扱うか

を考えます。

最も単純なのは、

壊れている
↓
削除する

です。

しかし、

公開後のゲームでは、

その方法を最初の選択にはしたくありません。

セーブデータには、

どこまで進んだか
どの依頼を終えたか
どの町を解放したか
どんな選択をしたか

という、

プレイヤーの旅が入っています。

そこで今回のWeb RPGでは、

current
backup
quarantine

という3つの役割を使って、

壊れたデータをすぐ消さずに扱うようにしました。


2. currentは「現在使っているセーブ」

まず、

currentです。

これは名前のとおり、

現在、そのスロットで通常利用するセーブデータ

です。

例えば、

slot_1
└ current

があれば、

通常のロードでは、

まずこのcurrentを確認します。

正常なら、

current
↓
validation
↓
valid
↓
ロード

です。


3. currentは常に正常とは限らない

ただし、

保存されているcurrentを、

無条件に信用するわけではありません。

例えば、

current
↓
JSON破損

や、

current
↓
必須項目欠落

が起きる可能性があります。

そのため、

第34回で扱ったvalidationを行います。

current
↓
validation
↓
valid?

という確認です。


4. currentが壊れていたらどうするか

例えば、

slot_1.current
↓
invalid

だったとします。

ここで、

削除
↓
新規ゲーム

にしてしまえば、

処理自体は簡単です。

しかし、

まだ復旧できる可能性があります。

そこで次に見るのが、

backup

です。


5. backupは「一つ前へ戻れる場所」

backupは、

現在のセーブが使えなくなった場合に備えて残す、

復旧候補

です。

考え方としては、

current
↓
現在利用するデータ

backup
↓
現在データに問題があった場合の復旧候補

です。


6. 上書き前にbackupを作る

例えば、

同じスロットへ新しいセーブを書き込むとします。

slot_1 current
↓
新しいGameStateで更新

このとき、

いきなり古いcurrentを消して、

新しいデータだけを書き込む方法もあります。

しかし、

新しい書き込みに問題があった場合、

元へ戻れません。


7. currentをbackupへ残してから更新する

そこで、

考え方としては、

旧current
↓
backupへ保存

新しいSaveData
↓
currentへ保存

とします。

これにより、

新current
↓
壊れている

backup
↓
正常

なら、

一つ前の状態へ戻れる可能性があります。


8. backupは単なるコピーではない

backupは、

画面上で通常ロードするための、

2つ目のセーブスロットではありません。

役割は、

通常利用
↓
current
復旧用
↓
backup

です。

つまり、

プレイヤーが自由に使う、

slot_1
slot_2
slot_3

とは別の概念です。


9. スロットと世代を分けて考える

例えば、

3つのセーブスロットがある場合でも、

内部的には、

slot_1
├ current
└ backup

slot_2
├ current
└ backup

slot_3
├ current
└ backup

のように考えられます。

つまり、

どの冒険か
↓
slot

その冒険のどの保存世代か
↓
current / backup

です。


10. currentが壊れたらbackupも検証する

ここで重要なのは、

backupがあるからといって、

すぐ復旧に使わないことです。

current
↓
invalid

backup
↓
存在する

だけでは足りません。

backupも、

validationします。

backup
↓
validation
↓
valid?

です。


11. backupも壊れている可能性がある

例えば、

current
↓
invalid

backup
↓
invalid

という場合もあります。

この状態で、

backupをそのままcurrentへ戻しても、

壊れたデータを別の場所へコピーするだけです。

そのため、

backupあり
=
復旧できる

ではありません。


12. 正常backupだけを復旧候補にする

基本形は、

current
↓
invalid
↓
backupを確認
↓
backup valid?
├─ Yes
│  ↓
│  復旧候補
│
└─ No
   ↓
   自動復旧しない

です。

ここでも、

存在することと、安全に使えることは別

です。


13. 壊れたcurrentをどうするか

正常backupが見つかったとしても、

壊れたcurrentを、

すぐ削除してしまう必要はありません。

むしろ、

そのデータは、

あとで、

なぜ壊れたのか

を調査する材料になるかもしれません。

そこで使うのが、

quarantine

です。


14. quarantineは「隔離」

quarantineは、

日本語にすると、

「隔離」

に近い考え方です。

正常なセーブとしては使わない

でも削除もしない

別の場所へ分けて残す

という役割です。


15. 壊れたデータを隔離する

例えば、

current
↓
invalid

だった場合、

そのまま通常ロード対象として残しておくと、

次回もまた同じデータを読もうとする可能性があります。

そこで、

invalid current
↓
quarantineへ移す

とします。


16. quarantineしたデータは通常ロードしない

隔離後は、

slot_1
├ current
├ backup
└ quarantine

のうち、

quarantineを通常ロード対象にはしません。

つまり、

quarantine
↓
通常ゲームからは使わない

です。

これで、

壊れたデータを、

誤って再利用しにくくなります。


17. なぜ削除しないのか

例えば、

壊れたデータに、

次のような情報が残っているかもしれません。

どのschemaだったか
どのappVersionで作られたか
どのslotだったか
どこまで正常だったか

これらは、

不具合調査に使える可能性があります。

削除すると、

確認できなくなります。


18. quarantineには「証拠を残す」意味もある

つまり、

quarantineには、

単に、

危険だから別の場所へ置く

だけではなく、

問題発生時の元データを残す

意味もあります。

公開後のアプリでは、

この違いが重要になります。


19. 復旧できる場合の基本フロー

正常backupがある場合を整理します。

current
↓
invalid
↓
quarantineへ隔離
↓
backupをvalidation
↓
valid
↓
backupから復旧
↓
新しいcurrent

です。


20. 先に壊れたデータを守る

ここで、

処理順序も重要です。

例えば、

backupをcurrentへ書き込む
↓
壊れたcurrentを失う

よりも、

先に、

壊れたcurrent
↓
quarantine

として残しておけば、

復旧処理の途中でも、

元データを失いにくくなります。


21. 復旧後のcurrentも検証する

backupが正常だったとしても、

復旧処理が終了しただけで、

すべて完了とは考えません。

考え方としては、

backup
↓
currentへ復旧
↓
currentを確認
↓
正常

まで確認します。

つまり、

復旧処理が動いたこと

と、

正常なcurrentが得られたこと

を分けます。


22. backupがない場合もある

しかし、

必ずbackupがあるとは限りません。

例えば、

初回保存直後

backup生成前

backup自体が失われた

backupも壊れている

といった場合です。

すると、

current
↓
invalid

backup
↓
利用不可

となります。


23. 復旧できなくても壊れたcurrentは隔離する

自動復旧できない場合でも、

考え方は同じです。

invalid current
↓
quarantine

として、

壊れたデータを、

通常のセーブから切り離します。


24. そのスロットを永遠に壊れたままにしない

例えば、

slot_2のcurrentが壊れ、

backupもなかったとします。

そこで、

slot_2
↓
使用不能

のまま固定してしまうと、

3スロットのうち1つが、

永久に使えなくなります。

そこで、

隔離後は、

通常のcurrentが存在しない状態

へ戻せます。


25. 空きスロットとして再利用できる

つまり、

壊れたcurrent
↓
quarantine
↓
currentなし
↓
空きスロットとして再利用

です。

ここでも、

quarantineに元データは残っています。

プレイヤーは新しいセーブを作れます。


26. 「復旧できない」と「スロットを使えない」は違う

整理すると、

自動復旧できない

からといって、

そのスロットを二度と使えない

とは限りません。

復旧できない
↓
元データを隔離
↓
通常領域を空ける
↓
新しいセーブに使える

という選択ができます。


27. 他のスロットは巻き込まない

例えば、

slot_1
↓
valid

slot_2
↓
invalid

slot_3
↓
valid

だったとします。

この場合、

問題があるのは、

slot_2です。

そのため、

slot_1
↓
そのまま利用

slot_2
↓
復旧/隔離

slot_3
↓
そのまま利用

とします。


28. 一つの破損でセーブ機能全体を止めない

もし、

1スロットが壊れただけで、

セーブ画面全体
↓
利用不能

となると、

正常なセーブまで使えなくなります。

そのため、

問題の影響範囲は、

スロット単位

に閉じ込めます。

これは、

第31回以降で繰り返してきた設計方針でもあります。


29. future schemaはquarantineしない

ここで、

第32回との違いを確認します。

例えば、

saveSchemaVersion = 99

のセーブを見つけたとします。

現在のアプリでは使えません。

しかし、

それは、

invalid

とは限りません。


30. unsupportedとcorruptedは別

future schemaは、

unsupported

です。

一方、

今回扱っているのは、

invalid

または、

corrupted

と判断したデータです。

つまり、

future schema
↓
quarantine

とはしません。


31. 知らないデータは触らない

future schemaでは、

現在のアプリが、

その形式を理解できません。

そのため、

読み込まない
上書きしない
Migrationしない
破損扱いしない

とします。

つまり、

何もしないことで守る

対象です。


32. 壊れているデータは隔離する

一方、

対応しているschemaとして検証した結果、

invalid

と判断したなら、

通常利用領域から外す必要があります。

そこで、

invalid
↓
quarantine

が使われます。


33. current/backup/quarantineの役割を整理する

ここまでをまとめると、

current
↓
通常利用する現在のセーブ
backup
↓
currentに問題が起きたときの復旧候補
quarantine
↓
通常利用しないが削除もせず残す隔離データ

です。

それぞれ、

明確に責務が違います。


34. 同じSaveDataでも「役割」が違う

データの内容だけを見ると、

currentbackupも、

同じSaveData形式かもしれません。

しかし、

保存基盤上では、

役割が違います。

同じデータ型
≠
同じ用途

です。

これは、

第33回で、

localStorageからIndexedDBへ移行するときにも重要でした。


35. 保存媒体を変えても役割を維持する

正式公開版では、

保存媒体を、

localStorage
↓
IndexedDB

へ移行しました。

そのときも、

単にセーブJSONだけを移したのではなく、

current
backup
quarantine

という意味を維持しています。

つまり、

保存先が変わっても、

復旧設計そのものは維持します。


36. currentを更新するときの流れ

保存処理を概念的に整理すると、

例えば、

次のようにできます。

現在のcurrentを確認
↓
必要ならbackupとして保持
↓
新しいSaveDataをcurrentへ保存
↓
currentを検証
↓
完了

実装方法は保存基盤によって変わります。

重要なのは、

新しいcurrentを書くだけの処理にしない

ことです。


37. 復旧処理の流れ

ロード時に問題が見つかった場合は、

例えば、

current取得
↓
validation
↓
invalid
↓
currentをquarantine
↓
backup取得
↓
backup validation
↓
valid?

となります。

正常backupがあれば、

backup
↓
currentへ復旧
↓
ロード

です。


38. backupがなければ隔離して終わる

正常backupがなければ、

invalid current
↓
quarantine
↓
currentなし
↓
slot再利用可能

とします。

無理に、

壊れたGameStateの一部を補完する

といった処理には進みません。


39. 自動修復をやりすぎない

例えば、

HPがない
↓
20を入れておく
currentTownIdがない
↓
最初の町を入れておく

のような修復も考えられます。

しかし、

それで元のゲーム状態を正しく再現できるとは限りません。

むしろ、

ロードはできた
↓
でも別の旅になった

可能性があります。


40. 復旧は推測ではなく正常データから行う

そこで、

今回の基本方針では、

壊れたcurrent
↓
推測して修復

より、

壊れたcurrent
↓
正常backup
↓
復旧

を優先します。

正常な復旧元がなければ、

無理にゲーム状態を作りません。


41. fail-closedをここでも使う

つまり、

安全な復旧元がある
↓
復旧する
安全な復旧元がない
↓
無理に復旧しない

です。

第32回まで扱ってきた、

安全だと確認できなければ先へ進めない

というfail-closedの考え方を、

破損復旧にも適用しています。


42. quarantineはユーザーの通常操作から隠す

隔離されたデータは、

通常のロード一覧で、

普通のセーブとして表示する必要はありません。

例えば、

セーブ1
↓
正常

セーブ2
↓
空き

セーブ3
↓
正常

と見せながら、

内部には、

slot_2 quarantine

が残っている、

という状態も可能です。


43. 内部で残すこととUIへ出すことは別

これは、

内部データとして保持する

ことと、

プレイヤーが通常操作できるようにする

ことを分ける考え方です。

quarantineは、

保持するが、通常利用しない

という中間状態です。


44. E2Eで確認したいシナリオになる

この仕組みは、

関数単位のテストだけではなく、

実際のゲーム操作として確認する価値があります。

例えば、

正常current
↓
破損させる
↓
正常backupを用意
↓
ゲームを開く
↓
破損検出
↓
backup復旧
↓
壊れたcurrentをquarantine

という流れです。


45. backupなしのケースも重要

もう一つは、

current破損
↓
backupなし
↓
quarantine
↓
slot再利用

です。

こちらも、

正常backupがあるケースとは、

別の仕様です。


46. 正式公開版でも両方を確認している

正式公開版では、

セーブ品質設計として、

  • 破損current+正常backupからの復旧
  • backupなし破損データの隔離
  • 隔離後のスロット再利用

まで実装しています。

またPhase Gでは、破損セーブ検出・backup・quarantine・復旧を独立した正式完了項目として受入しています。


47. 「削除しない」が品質設計になる

セーブデータが壊れたとき、

もっとも単純なのは、

エラー
↓
削除

です。

しかし、

公開後のゲームでは、

それまでの進行自体に価値があります。

そこで、

正常なら使う

壊れていたら隔離する

backupが正常なら戻す

戻せなくても元データは残す

他のスロットは使える

という設計にしました。


48. 今回のポイント

今回のポイントは3つです。

  • currentは通常利用する現在のセーブ、backupは復旧候補、quarantineは壊れたデータを削除せず隔離する場所
  • currentが壊れていても、正常backupがあれば復旧し、元の壊れたcurrentはquarantineへ残す
  • backupがなくても、壊れたcurrentを隔離することでスロットを再利用でき、他の正常スロットも巻き込まない

整理すると、

current
↓
invalid
↓
quarantine

backup
↓
valid?
├─ Yes
│  ↓
│  currentへ復旧
│
└─ No
   ↓
   currentなし
   ↓
   slot再利用

です。

セーブを守るというのは、

必ず元の状態へ戻せること

だけではありません。

壊れたものをすぐ捨てず、戻せるなら戻し、戻せなくても被害を広げない

ところまで含めて、

復旧設計だと考えています。


49. 次回

次回は、

壊れたcurrentをbackupから復旧する――Recoveryの実装フロー【第36回】

です。

今回は、

current
backup
quarantine

それぞれの役割を整理しました。

次回は、

current破損
↓
quarantine
↓
backup validation
↓
current復元
↓
再validation
↓
ロード

という一連のRecoveryを、

もう少し実装寄りに見ていきます。

特に、

どの順序で処理するか
途中で失敗したらどうするか
復旧後に何を検証するか

を整理します。


この記事を最後まで読んでいただき、ありがとうございます。
いいね」を押していただけるとうれしいです。これからの記事づくりの励みになります。
もし気に入っていただけましたら、フォローもよろしくお願いします。


前の記事

第34回 壊れたセーブデータをどう検出するか

次の記事

第36回 壊れたcurrentをbackupから復旧する――Recoveryの実装フロー

連載トップ

第0回 React+TypeScriptで異世界RPGを作る――連載の目的と開発ロードマップ

note関連記事

壊れたセーブデータから、どう復旧するか #20

noteでは、壊れたセーブをすぐ削除せず、currentbackupquarantineを使って守る理由や、backupがない場合にスロットを再利用できるようにした背景を制作側の視点から書いています。([note][1])

note「壊れたセーブデータから、どう復旧するか #20」を読む

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?