あっ、もしかして削除した.........？との戦い - 履歴のしくみ

[やらかしちゃった](https://qiita.com/advent-calendar/2020/yarakashi-production) ときのテッパンネタといえば「削除してしまった」。
[crontab database ～君がしでかしてくれたもの～](https://qiita.com/raki/items/8aab477b4d64c7e7610e) から、
今年は [ホスティングサーバの/binディレクトリをrm -rfしてしまったおじさんの昔話](https://qiita.com/real_yaruo/items/7e2eb9a28d42b3165c83)
[本番でTableを1つDeleteしてしまいON DELETE CASCADEでさらに4つTable dataが消えた話](https://qiita.com/nobu0605/items/75793a4db539f054b28d)
[管理者用初期化URLを踏んでWebサービスのデータをふっとばした話](https://qiita.com/HirotoKagotani/items/181052eb85b686783806#%E4%BD%95%E3%81%8C%E6%B6%88%E3%81%88%E3%81%9F%E3%81%AE%E3%81%8B) がなかなか胃に来る。

## 結論：手段はある

しかし、人類は努力してきたのだ。がこの記事の結論です。
**「履歴」**の話です。誤削除にまつわる人類の工夫の足跡、安全な設計を考えたい人のヒントになるかもしれない。

### 序：消したくないのに消えるものと戦ってきた人々
#### [GoogleDriveのファイルがなくなる...](https://qiita.com/clutch/items/fb0387ba288faddaf226)

ざっくり、ファイルの権限手続きに伴いアクセスが不穏になる話というのがあります。

> 退職者アカウントを削除するときです。
> アカウントを削除する際には、そのユーザが所有しているファイル等を他のユーザに譲渡するか確認されます。

> ここで共有されているファイルがあるにもかかわらず、権限譲渡を行わないままユーザを削除すると悲劇が起きます。該当ファイルを使用していた他のメンバーからファイルがキレイになくなります。

#### 消すつもりはないのに消えたCtrl+Z...

Windowsにも、欠陥と言われる動作があります。
[Windows 10の「Ctrl＋Z」にご用心　コピーしたファイルを編集してから押すと無警告で“完全削除”される](https://nlab.itmedia.co.jp/nl/articles/1712/06/news135.html)、[windows10 エクスプローラーの欠陥について。Ctrl+Z → Ctrl+Y でデータ消失](https://answers.microsoft.com/ja-jp/windows/forum/windows_10-files-winpc/windows10/94d36c8b-e1fc-49e8-93b1-16f60c66bd83)

以下の手順を踏んだ際に発生します。

> ファイルをコピーする
> コピーしたファイルを編集する
> 編集したファイルを上書き保存して終了する
> エクスプローラ上で「Ctrl＋Z」をする
> 編集したファイルが消える

やってみたら確かに消えます。（笑い事ではない人すみません。）

#### DELETE FROM テーブル名...

[なぜRDB（リレーショナルデータベース）は「DELETE FROM テーブル名」で**うっかりwhere句をつけ忘れるとテーブルのデータが全削除されるような危ない仕様になっているのでしょうか？**](https://jp.quora.com/%E3%81%AA%E3%81%9CRDB-%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9-%E3%81%AF-DELETE-FROM-%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E5%90%8D-)
...！？等という質問。

「それは『なぜUnix系OSはrootでrm -rf /とタイプするだけで惨劇が起きるような仕様なのでしょうか？』という質問と似ていますね。」ーーー。

> むしろ全レコード削除はいいほうで、UPDATEでWHEREをつけ忘れても同じことが起きますが、こちらのほうがデータの一貫性が失われ、システムが動いている間にインシデントが起きると他のテーブルのデータにまでデータの汚染が波及する可能性があり、もっと悲惨です。

> 手動でのデータ変更はトラッキングできず、再現性がありません。

と回答されている。人々は間違えることはある。いやだからこそ、誤った行動を防ぐために人々は知恵を絞ってきた。


## 破：知恵、それは論理削除

人類は消しても消えないように工夫してきた。論理削除というやつです。
[論理削除と物理削除](https://qiita.com/jonson29/items/4743409eda08fcdf5410)
[1分でわかる論理削除 -メリットとデメリットを考える-](https://qiita.com/wanko5296/items/f1af9c7bf020e867c2dd)
[削除機能の設計(物理削除とは/論理削除とは)](https://qiita.com/shukan0728/items/1a7b2db01ae25f8f7f88)

なぜ論理削除が必要なのか：

> ユーザーからその存在を隠蔽したいが、データとしては残しておきたい （例えば、業務事情により当該データを使用禁止にしたいが、一時的なのですぐに元に戻せるようにしたい場合）
> 誤って削除した場合に備え、**すぐにデータを復元できるようにしておきたい**

一方で論理削除は人類には難しい：

> バグを生み出す原因になる (例：WHERE句への論理削除フラグの付け忘れ)
> データ量増大やそれに伴う検索性能の劣化　※論理削除データが多い場合

つまり通常のデータ（未削除のデータ）と削除済みのデータが1つのテーブルに、たとえば**履歴化されて**混在するため、**削除したデータ量が増える**とそれに伴い検索**性能が劣化する**。また不整合データを作成しないように履歴を壊さないように業務アプリ側も慎重に慎重に工夫しないと、また不具合となる。それだけ知恵を絞らないといけないことなのです。以下、事例。


### ごみ箱への削除

Wikipedia「[ごみ箱](https://ja.wikipedia.org/wiki/%E3%81%94%E3%81%BF%E7%AE%B1_(GUI))」：

> ごみ箱が単なる削除と異なるのは、実際に削除されるまではファイルの削除を取りやめる事ができるという点である。これにより誤操作によるファイル消失を防ぐ事ができるほか、情報という目に見えないものを扱ったファイルを、実体を持った対象として認識させる心理的な作用も働く。また多くは ファイルアイコンをドラッグ操作でごみ箱へと移動するため、操作概念が明確で誤りが起こりにくい。

メールボックスなどでも、メールを削除したけど実際はユーザにも分かる形で移動しているので自分でも戻せる。更にGmail なら、[ユーザーの完全に削除されたメールを復元する](https://support.google.com/a/answer/112445?hl=ja) 事もできる。完全に消しているわけではない。

Windowsの[ごみ箱の実体](https://michisugara.jp/archives/2013/trash_can.html#i-2) は「$Recycle.Bin」という特殊な仮想フォルダ。
何なら [Windowsの「ごみ箱」を作業フォルダにしてる人がいる？　意外な使い方がネットで話題に](https://nlab.itmedia.co.jp/nl/articles/1510/30/news109.html) という、端的に「ごみ箱」を作業フォルダとしてすでに使う運用をしている者もいるようだ...。
消しても消えないようにできているのである。

### 削除後のUndo, ロールバック

[なぜ私はVimを使い始めたのか：アンドゥ機能事始め](https://qiita.com/okuramasafumi/items/c65e0efaffe00bea48c4)
アンドゥの**履歴**をツリー状にかつ無制限に保存できる。

だけでなく[「Windows更新プログラムの構成に失敗しました。変更を元に戻しています」って、これってMicrosoftのバグですか？ とても迷惑です…。](https://jp.quora.com/Windows-koushin-puroguramu-no-kousei-ni-shippai-shima-shita-henkou-wo-moto-ni-rei-shi-tei-masu-tte-kore-tte-Microsoft-no-bagu-desu-ka-totemo-meiwaku-desu) なんていうのを見ることがありますね。

> Windows Updateやインストーラーの類が変更を元に戻すロールバックモードに入るということは、想定しなかった事態が起き、このまま進めることができない状態になったケースが大多数であると考えられます。

> 例えば、インストーラーが上書きしようとした新しいファイルが何らかの形でロックされていて上書きできなかった、ディスク容量が不足していた、ディスクやファイルが破損していた、インストーラーが削除しようとした古いモジュールが何らかの依存関係により削除できなかった、などというケースが多いと考えられます。

PCはなるべくきれいに使おう、に尽きるのだけど、そんな人を救う策としてロールバックの機構が装備されているとも思うところなのです。


### アカウント削除後のアカウント

[なぜSNSのアカウントを削除しても随分経った後に復元できてしまうのですか？これは削除したと言えるのでしょうか？](https://jp.quora.com/%E3%81%AA%E3%81%9CSNS%E3%81%AE%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%A6%E3%82%82%E9%9A%8F%E5%88%86%E7%B5%8C%E3%81%A3%E3%81%9F%E5%BE%8C%E3%81%AB%E5%BE%A9%E5%85%83)
SNSも、過去アカウントは復活できるようになっているようだ。

Twitter [アカウント復活のヘルプ](https://help.twitter.com/ja/managing-your-account/trouble-reactivating-twitter-account)
Facebook [ログインできない古いFacebookアカウントの利用を再開するにはどうすればよいですか。](https://www.facebook.com/help/231208473756221)
なぜかというとこれは上記Quora：

> 念の為、って感じだと僕は認識しています。

> もしかしたら、アカウントを削除したユーザが問い合わせてきて、そのアカウントを復元させてくれと言いだすかもしれないですし、サービス内で統計情報などを作る際にも使われるかもしれません。

> つまりサービス内でアカウントを削除したとしても、DB上から完全に削除されることは稀だということです。なるべくサーバーに負荷を与えず、処理を行うことを考えれば、DBから全てのデータを削除するのではなく、ある値（deleteDataとします）が0であれば、削除されていない状態、1であれば削除されている状態。

との回答。サーバに負荷を与えることなく、アカウントを使用できない状態にする。
なるほど中身はともあれアプリケーション設計上、一般的に行われているものとして想像がつきます。

### 削除後のFrashback query

さらにそんな開発者を救うことがある機構。冒頭に `DELETE FROM テーブル名` の話題を出したが、それでもフラッシュバッククエリというものが効くことがある。
http://oracle-abc.myoa.info/flashback-query

```sql
SELECT * FROM テーブル名
AS OF TIMESTAMP TO_TIMESTAMP('2021-01-08 01:00:00', 'YYYY-MM-DD HH24:MI:SS');

SELECT * FROM テーブル名
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '2' HOUR);
```

Postgresにも昔そのようなものがあったらしい。
https://dba.stackexchange.com/questions/359/what-other-databases-have-a-feature-similar-to-oracles-flashback-query/362#362

### rm後の復元

[削除したファイルをlsofで復元する](https://www.itmedia.co.jp/enterprise/articles/0611/30/news007.html)
[Linuxにて削除(rm)してしまったファイルを復元する【CentOS】](https://qiita.com/satoken0417/items/c1535b7d3322ac0d69ca)
「今度は間違えないようにcpやらmvやらしてあげてくださいねー！」とあります。

[ハードディスク内のデータを本当の意味で完全消去する方法](https://qiita.com/noraworld/items/72d6a6dc5014f44e789f) なんていうナレッジもある。
意外と、大概のものは消えない。

### 削除後のRevert

最後にお馴染みGitの例。削除しても削除したという**履歴**がむしろ残る。Revert。
[【gitコマンド】いまさらのrevert](https://qiita.com/chihiro/items/2fa827d0eac98109e7ee)

> 既存のコミットを取り消すためのコマンドです。
> 「取り消したいコミットを打ち消すようなコミットを新しく作成する」という処理によって、既存のコミットを取り消します。新しくコミットを追加しているだけなので、既存コミットの履歴が消えるわけではありません(コミットログをみると残っています)。
> どんな変更があったのかということが(revertしたということも含めて)残るので、リモートにpushされて公開されているコミットに対しても安全に使うことができます。

テクニックは色々あります。
[Gitでやらかした時に使える19個の奥義](https://qiita.com/muran001/items/dea2bbbaea1260098051)
[git 上のいろんなことをなかったことにする](https://qiita.com/mdstoy/items/6de0aaa06dcd0cc46102)
[Gitで直前のコミットをなかったことにする](https://qiita.com/shosho/items/3c30160b1fa7b228eea6)

というか[Gitを領収書の管理で使いたいので税務署に電話してみた](https://kuxumarin.hatenablog.com/entry/2021/01/10/064740) なんて人もいる...

そう。**「履歴」**こそ、人類が自らの誤削除と戦って編み出してきた知恵なのです。


## 急：結論

それでも消しちゃうのが人間ってもので、いやー、アプリケーション設計開発って本当に難しいですね～、という何ともな世間話的結論になってしまうのですが、[エンジニアなら知っておきたい障害報告&再発防止策の考え方](https://qiita.com/hirokidaichi/items/f9f4549c88aaf8b38bda) もあるのでここでは以下引用。冒頭で紹介した[管理者用初期化URLを踏んでWebサービスのデータをふっとばした話](https://qiita.com/HirotoKagotani/items/181052eb85b686783806#%E4%BD%95%E3%81%8C%E6%B6%88%E3%81%88%E3%81%9F%E3%81%AE%E3%81%8B) にはこんなことが書かれていました。

> あわててはいけません。まずは落ち付きます。今なら「全集中、鯖の呼吸」とつぶやくべきところです。たぶんサーバと一体化し、最善の一手となるキーに一筋の光が見えます……（知らんけど）

> **最後の手段はある。これは気分として重要です。**

うっかり削除と戦ってきた我々人類、最後の手段はあります。消した事実は消えないが、黙ってバックアップ（あるよね？）から復旧しよう。そしてよい[再発防止策](https://qiita.com/hirokidaichi/items/f9f4549c88aaf8b38bda#%E8%89%AF%E3%81%84%E5%86%8D%E7%99%BA%E9%98%B2%E6%AD%A2%E7%AD%96) を考えよう。参考まで良い再発防止策とは、

1. その種類の問題について二度と意識することがなくなる解決策
2. その種類の問題を開発時に自動的に検知することができる解決策
3. その種類の問題が発生しても自動的に復旧することができる解決策
4. その種類の問題が発生しても影響が局所化される、フールプルーフ、フェールセーフになる解決策

[と書かれています](https://qiita.com/hirokidaichi/items/f9f4549c88aaf8b38bda)。

消しても消えない安全なアプリケーション設計生活を祈りたい。
以上趣味による、削除と戦ってきた **履歴** 設計事例まとめでした。