Delta Lake のログ・過去データの保持期間について理解する

　Delta Lakeはストレージ層としてスケーラブルかつ安価なクラウドストレージ(S3, ADLS, GCS等)を使用しながらも、ファイルをテーブルとして操作・し、テーブル毎のバージョン管理や、過去バージョンへの遡り([タイムトラベル](https://delta.io/blog/2023-02-01-delta-lake-time-travel/))を行うことができます。

　この記事では、Delta Lakeのテーブル（以下、Deltaテーブル）のタイムトラベルはどのくらい過去の状態まで遡ることができるのか？遡れる期間を変更するにはどうすれば良いか？について解説します。

# タイムトラベルに必要な物
　まず、Deltaテーブルがタイムトラベルで過去の状態に遡るためには何が必要なのか？を整理すると、以下の2つになります。

* デルタログ（トランザクションログ）
* 過去の状態のデータが入ったParquetファイル

　Deltaテーブルは内部ではParquetファイルを使ってデータを保持していますが、Deltaテーブルに対して`INSERT`, `UPDATE`, `DELETE`等のトランザクションを加えても、既存のParquetファイルが直接上書きされたり削除されたりはしません。その代わり、挿入、変更、削除後の新しい状態を保持したParquetファイルが新たに作成され、読み取り対象のParquetファイルがそちらに切り替わるという動作をします。

　デルタログというのはトランザクションログに相当するもので、各トランザクションにおいて新たに追加されたParquetファイルがどれで、不要になった(参照されなくなった)Parquetファイルがどれか、という情報を記録しています。

　タイムトラベルを使って指定した過去のバージョン（またはタイムスタンプ）まで遡るというのは、指定された過去バージョンで参照されていたParquetファイルがどれなのかをトランザクションログから判断し、それらのParquetファイルを読み取って過去の状態を再現するということです。

　したがって、**タイムトラベルで遡ることのできる期間をコントロールするためには、過去のParquetファイルとデルタログ両方の保持期間について理解する必要があります。** 少しややこしいのですが、この2つはそれぞれ考え方やデフォルト設定が異なっているので分けて説明します。

# デルタログの保持期間
　まずはデルタログの保持期間についてです。

* **デルタログの保持期間は、`delta.logRetentionDuration`という設定で決まります。**
* デフォルトは **30日間** に設定されています。
* 設定は基本的にテーブル単位で行います。

　この保持期間の変更は以下のようにして行うことができます。

```SQL:delta.logRetentionDurationの変更
ALTER TABLE test_table SET TBLPROPERTIES(delta.logRetentionDuration = "interval 40 days");
```

　上記の例のように40日間で設定した場合、少なくとも40日間はデルタログを保持されるようになります。

　一方、きっかり40日間が経過した時点で即座に削除されるかというと、そうでもありません。Deltaテーブルの実体はログも含めてクラウドストレージ上にあるただのファイルなので、そもそも該当のDeltaテーブルに対して何もトランザクションを行っていなければ、ログが勝手に消えることもありません。また保持期間経過後に新たなトランザクションが行われたとしても、古いログが実際に削除されるためには内部で様々な条件があり、チェックポイントファイルが作成されていることや、そのチェックポイントの保持期間等も条件に含まれます。このようにデルタログが実際に削除されるタイミングは複雑な条件によって決まります。ただし、`delta.logRetentionDuration`で設定した保持期間内のデルタログは必ず保持されます。

# 過去のParquetファイルの保持期間
　次に過去のParquetファイルの保持についてですが、**過去のParquetファイルは[`VACUUM`](https://delta.io/blog/2023-01-03-delta-lake-vacuum-command/)コマンドで明示的に削除処理を行わない限り、勝手に消えることはありません。** したがって乱暴に考えれば、`VACUUM`コマンドを一切実行しなければ過去全てのParquetファイルを保持しておくことができます。ただしそうするとストレージの容量が青天井で膨らんでいってしまいます。そのため実際には定期的に`VACUUM`を行って、意図した保持期間を超えた不要なParquetファイルは削除するのがベストプラクティスです。

### VACUUM の RETAIN オプション
　VACUUMを行いつつ、過去のParquetファイルを必要な保持期間分だけ維持する方法として、`RETAIN`オプションがあります。`VACUUM`を行う際には、以下のように `RETAIN`オプションを付けて保持期間を指定することができるようになっています。`RETAIN`で指定した保持期間以内のParquetファイルは、古い（既に最新バージョンからは参照されていない）Parquetファイルであっても削除されません。

```SQL:VACUUMの例
VACUUM test_table RETAIN 336 HOURS
```

　上の例では`RETAIN 336 HOURS`が付いているので、過去336時間（=14日間）以内にその時点の最新バージョンから参照されていたParquetファイルは必ず保持されます。
　ただし、VACUUMの度にRETAINを正しく指定すればそれで良いのですが、もしこれを誤るとまだ保持しておきたい過去Paquetファイルを消してしまうことになります。このような誤りを防ぐための設定が、次の`delta.deletedFileRetentionDuration` です。

### 誤ったVACUUMの防止
* **`delta.deletedFileRetentionDuration`は、`VACUUM`の`RETAIN`オプションによって指定できる最短の保持期間を制限する設定**です。
* デフォルトは **7日間** に設定されています。
* 設定は基本的にテーブル単位に行います。　

デフォルトの7日間だと、たとえ`VACUUM`の`RETAIN`で7日間より短い保持期間した際にエラーとなります。従ってこの設定を必要な保持期間に変更しておけば、保持期間内であるべき過去Parquetファイルの誤削除を防止できます。設定の変更は以下のようにして行います。

```SQL:delta.deletedFileRetentionDurationの変更
ALTER TABLE test_table SET TBLPROPERTIES(delta.deletedFileRetentionDuration = "interval 1 days");
```

上の例では1日間（24時間）で設定しています。これに対して、誤って24時間より短い期間を`RETAIN`で指定して`VACUUM`を実行してしまったとします。

```SQL:VACUUMの例
VACUUM simple_tbl RETAIN 20 HOURS
```

そうすると、以下のような感じのエラーが出力され、誤った`VACUUM`を防いでくれます。

```text:VACUMMエラー
Error in SQL statement: IllegalArgumentException: requirement failed: Are you sure you would like to vacuum files with such a low retention period? If you have
writers that are currently writing to this table, there is a risk that you may corrupt the
state of your Delta table.

If you are certain that there are no operations being performed on this table, such as
insert/upsert/delete/optimize, then you may turn off this check by setting:
spark.databricks.delta.retentionDurationCheck.enabled = false

If you are not sure, please use a value not less than "24 hours".
```

ちなみにこのエラーの中で登場している`spark.databricks.delta.retentionDurationCheck.enabled`は、`false`に設定すると`delta.deletedFileRetentionDuration`の設定が無視されるようになる、というものです。せっかく設定した保持期間を無視したVACUUMを許してしまうことになるため、デフォルトは`true`であり、`false`にするのは非推奨です。

# 設定例
　以上のことを踏まえて、例えば過去90日間まではタイムトラベルで遡れるようにしたい、という場合は以下のように設定すれば良いです。

```SQL:delta.deletedFileRetentionDurationの変更
ALTER TABLE test_table SET TBLPROPERTIES(delta.logRetentionDuration = "interval 90 days");
ALTER TABLE test_table SET TBLPROPERTIES(delta.deletedFileRetentionDuration = "interval 90 days");
```

この設定により、以下の状態になります。

* `delta.logRetentionDuration`が90日間になっているので、デルタログは必ず90日間以上保持される。
* `delta.deletedFileRetentionDuration`も90日間になっているので、直近90日間以内の過去Parquetファイルは`VACUUM`でも削除されない。

# 留意すべき点
テーブルのサイズやトランザクション頻度に依存はするのですが、特に過去Parquetファイルの方は保持期間を長くするほどクラウドストレージの容量も膨らんでしまいます。そのため、基本的には最小限の保持期間（多くの場合はデフォルト設定）での運用を意識しつつ、どうしても必要な場合だけ上記のように保持期間を延ばすことを検討するのが良いと思います。

# 参考URL
[Delta Lake Time Travel](https://delta.io/blog/2023-02-01-delta-lake-time-travel/)
[Delta Lake Vacuum Command](https://delta.io/blog/2023-01-03-delta-lake-vacuum-command/)
[Data Retention](https://docs.delta.io/latest/delta-batch.html#data-retention)
[Work with Delta Lake table history](https://docs.databricks.com/delta/history.html)
[Diving Into Delta Lake: Unpacking The Transaction Log](https://www.databricks.com/blog/2019/08/21/diving-into-delta-lake-unpacking-the-transaction-log.html)