ファイル破損でAthena検索ができなくなった時の対処


とある行ログをLogstashでJSONに変換してGzip圧縮後にS3にPUTし、Athenaで検索できるようにしていた。

LogstashのS3 outputプラグインはrestoreオプションをTrue (デフォルト) にしておくと、再起動時に前回途中まで処理したログをPUTしておいてくれる。
しかし、これに何かの原因で失敗すると、S3には不完全なログファイルがPUTされることがある。
そしてこれはGzip的には破損したファイルとなる可能性がある。

検索中に破損したファイルに遭遇すると、Athenaはエラーを出す。

HIVE_CURSOR_ERROR: Unexpected end of input stream

しかし、どのファイルが破損しているのかは教えてくれない

なので、Athenaで検索をかけたい場合、restoreオプションは誠に遺憾ながらFalseにしておくのが良いのではないかと思う。
リストアされないのでログに欠落が出る可能性があるが、とはいえファイルが破損すると検索できないというトレードオフ。
この場合、例えば (今回みたいに) 1時間でローテートする設定にしておくと、最長1時間分のログが欠落する。
1時間「だけ」と考えるか、「も」と考えるかが問題だ。

まぁお金の節約のためにもパーティションとローテーションは適切に設定しておくしかない、ってそれはいつだってそう。

今回はしかたがないので、ローカルでgunzipして破損したログファイルを特定する羽目になった。

How

エラーが出たパーティション下のデータ量を調べる

aws s3 ls log-bucket/log/yearmonth=201707/ --recursive --human --sum

何とかローカルに入りきりそうだったので

aws s3 sync s3://log-bucket/log/yearmonth=201707/ .

適当なスクリプトを作ってエラーチェック

#! /bin/bash

for f in $(ls *.gz); do
  gunzip $f
  if [ $? = 0 ]; then
  rm $(basename $f .gz)
  else
  echo $f
  fi
done

どれかのファイルでUnexpected end of streamが出たりすると思う。
破損ファイルを特定したらリダイレクトで解凍できるところまで解凍する

gunzip < ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt.gz > ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt

JSON行の途中で終わってたりすると思うので、編集

vim ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt

再度圧縮

gzip ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt

アップロードしなおし

aws s3 cp ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt.gz s3://log-bucket/log/yearmonth=201707/ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt.gz

これで完了。yearmonth=201707パーティションが検索できるようになった。

解凍後のファイルサイズが大きすぎて編集できない場合は、諦めてDELETEするか、「きっと最終行だけがおかしいに違いない」と運を天に任せてシェルで最終行だけ削除してみるとか、何か便利なアレとかコレとか使ってファイルを修復する。

ローカルに入りきらないよ

1個ずつダウンロードして確かめるとか……

いや、ファイル1個がローカルに入りきらないんですよ

そんな巨大な単一ログファイルをAthenaで検索するのはちょっとイケてない気がします。
ファイル分割してパーティション切ったほうがお得だと思います。