前提
- gz は一つのファイルを圧縮する形式であり、ディレクトリは圧縮できない。ディレクトリを圧縮する場合は一度 tar を作る必要がある。
- gz ファイルを二つ concatenate すると、二つのファイルの内容がまとまった一つのファイルが圧縮されたものが作成される
$ echo aaa > file1 && echo bbb > file2 $ gzip file1 && gzip file2 $ cat file1.gz file2.gz > combined.gz $ gzip -d combined.gz $ cat combined aaa bbb
- tar.gz ファイルを二つ concatenate しても、解凍した時に二つの tar の内容が merge された結果とはならない
- 一つ目の tar の中身のみが出力される
- 理由は、tar の構造 external_linkの最後には EOF を示す empty block (1024 bytes) があり、これにより最初の tar のみが認識されている
- 最初の tar から EOF を消して gz したものを concatenate すると、二つの tar が merge された結果が得られる
$ mkdir -p dir1 && echo aaa > dir1/test1.txt && echo bbb > dir1/test2.txt $ mkdir -p dir2 && echo ccc > dir2/test3.txt && echo ddd > dir2/test4.txt $ tar -cvf archive1.tar dir1/test1.txt dir2/test3.txt $ tar -cvf archive2.tar dir1/test2.txt dir2/test4.txt # archive1.tar の EOF を削除 $ file_size=$(wc -c < archive1.tar) $ new_size=$((file_size - 1024)) $ truncate -s $new_size archive1.tar $ gzip archive1.tar && gzip archive2.tar $ cat archive1.tar.gz archive2.tar.gz > combined.tar.gz $ tar -xzvf combined.tar.gz $ mkdir -p combined && tar -xzvf combined.tar.gz -C combined $ ls combined/dir1 test1.txt test2.txt $ ls combined/dir2 test3.txt test4.txt
再開可能な tar.gz での bulk export
以下を stream pipeline で実行する
- ID で指定された page について、その page と子ページ (user が閲覧権限を持っているもの) を読み込む
- page を tar に追加していき、3 で作成する tar.gz のサイズが閾値を超えたら締め切り、次の tar を作成する。最後の tar 以外は EOF をつけない。
- tar を gz で圧縮する
- 各 tar.gz を multipart upload で同じアップロード先にアップロードする
- 最終的なアップロードデータとしては複数の tar.gz が concatenate された状態と同じになるはず
中断された export を再開する場合は、最後に作成されていた、アップロードされていない tar.gz の part を作り直すところから始めることができる。
懸念点
- ファイル単位でアップロードする part が区切られるため、閾値を超える前の最後に追加されたファイルサイズが大きい場合、サイズが大きい part が生じる可能性がある
- ただし、一つのファイルが圧縮後の tar.gz ファイルサイズに与える影響はそこまで大きくなそう
- 一つの tar を一気に圧縮した場合と、分割してそれぞれ圧縮した場合で圧縮率はどれくらい異なるのか
- 手元で試してみたところ、3.28GB の 24.2MB に圧縮されるデータを三分割(均等ではない)してそれぞれ圧縮してみると、9.5MB, 7.2MB, 7.2MB (合計 23.9 MB) となった
- stream pipeline 処理の中での情報のやり取り
- 3 から 2 への tar.gz のファイルサイズの伝達など、stream 間の情報のやり取りが複雑になりそう
- 処理の全体像が見えない中で、tar に EOF をつけるかどうかをどのように判断するか
- tar の EOF をつけずに結合するという、イレギュラーな操作をする必要がある
- gcs の multipart upload は part が 256KB の倍数である必要があるが、ファイル単位で part のデータが区切られている必要があるので、そのままだとキリのいい数字にはならなさそう