👻

fluent-plugin-bigquery の設定

2020/10/01に公開

fluentdとfluent-plugin-bigqueryを使ってBigQueryにデータを投入する場合の設定について。

BigQueryの制限

2015/04/16に制限の変更がありました公式発表, 日本語のまとめ情報)。これにより、本記事の内容も影響を受けますので、新しい制限値を参考の上、適宜調整してください。

  • 1行20KBまで
  • 1リクエスト500行まで
  • 1リクエスト1MBまで
  • 1秒10,000行まで (申請により100,000まで)
  • 1秒10MBまで

参照: https://cloud.google.com/bigquery/quota-policy#streaminginserts

1リクエスト1MBを10リクエストで10MB、1リクエスト500行を20リクエストで1万行。

このどちらかが1秒あたりのリクエスト数の上限となる。

例えばサーバのアクセスログであれば1レコードのサイズは小さいため、10MB制限より先に1万レコードの制限に達すると思われる。よって、秒間のリクエストが20を超えないようにしておくとよい。

バッファの設定

  • buffer_chunk_records_limit
  • buffer_chunk_limit
  • buffer_queue_limit

fluent-plugin-bigqueryはチャンクを1リクエストとしてBigQueryのInsert APIを呼び出す。よって、BigQuery側の制限に違反しないようにプラグイン側を適切に設定する必要がある。

buffer_chunk_records_limit

チャンクごとの最大レコード数。

buffer_chunk_records_limit 500

BigQuery側で「1リクエスト500行まで」という制限があるので500とする。

500ちょうどにしておくとエラーになることがあるという情報があるので、300程度にしておくとよいかもしれない(コメント欄参照)。

buffer_chunk_limit

チャンクごとの最大バイト数。

buffer_chunk_limit 1000000

BigQuery側で「1リクエスト1MBまで」という制限があるので1000000とする。

1MBちょうどにしておくとエラーになることがあるという情報があるので、768k程度にしておくとよいかもしれない(コメント欄参照)。

buffer_queue_limit

プラグイン側でいくつのチャンクをメモリに保持しておくか設定できる。

デフォルト値は1024となっている。

小さすぎるとキューが溢れてエラーとなってしまうので、メモリ量と相談して大きめの値にしておくとよい。

1チャンクの最大サイズが1MBなので、デフォルトの1024だと最大1GBを使うことになる。ただ、buffer_chunk_records_limit が500と小さいので、実際には1チャンクが1MBになるわけではない。

例えばnginxなどのアクセスログで1レコードが 0.2 - 0.4KB 程度になる場合だと、1チャンクは 0.4KB * 500 = 200KB となる。1GBをバッファに使えるのであれば5000チャンクほどにできる。

送信の設定

  • flush_interval
  • try_flush_interval
  • queued_chunk_flush_interval
  • num_threads

flush_interval

バッファに溜まったレコードをflushする間隔。

デフォルト値は0.25となっている。

これはbuffer_chunk_records_limitbuffer_chunk_limitに達していないバッファ中のレコードをflushする間隔となる。

高レートの場合はbuffer_chunk_records_limitbuffer_chunk_limitの制限に先に達してflushされるので、この値の影響は小さい。

低レートの場合だと、これが送信の間隔になる。レコードをBigQuery側で即座にクエリしたい用途では短くする意味がある。

BigQueryは、リアルタイムにクエリというよりはバッチ処理の用途で用いられるため、1秒あるいはもっと長い値でも多くのケースで問題ない。

try_flush_interval

送信待ちとなったチャンクを、実際に送信する間隔。
言い換えると、送信待ちチャンクが存在するかどうかを確認する間隔。

デフォルト値は0.05となっている。

flush_intervalは「バッファに溜まったレコードを送信待ちに移す間隔」、try_flush_intervalは「送信待ちのチャンクの有無を確認する間隔」となる。

buffer_chunk_records_limitbuffer_chunk_limitに達したチャンクは送信待ちになるが、これらのチャンクが送信されるまでに最大でtry_flush_interval秒かかることになる。

ただ、実際にはBigQueryへのHTTP POSTのほうが時間がかかることで待たされるため、try_flush_intervalの影響を受けないケースも多い。

例えばBigQueryで0.5秒かかるとすると、その処理が終わった時はtry_flush_intervalの0.05秒はすでに過ぎているため、即座に次の送信が始まることになる。

queued_chunk_flush_interval

複数のチャンクが存在する場合に、あるチャンクの送信の後に次のチャンクの送信までの待ち時間。

デフォルト値は1となっている。

num_threads

バッファを処理するスレッド数。

デフォルト値は1となっている。

BigQueryのAPIの呼び出しは、外部サービスへのHTTP POSTのため一定の時間がかかる。このため複数のスレッドで実行すると効率がよい。

設定例

BigQueryにレコードを送信する量より、レコードを受信する量が多いと、バッファが溢れてしまうので、これが発生しないようにする。

ピーク時で秒間1000行が送られてくる場合を考えてみる。

1秒で1000行なので、1秒で2チャンクが溜まることになる。

HTTP POSTに0.5秒かかるとして、queued_chunk_flush_intervalがデフォルトの1のままだと、2チャンクの送信には2秒かかる。2チャンクだとHTTP POSTだけで1秒以上かかることもあるので、queued_chunk_flush_intervalが0でもバッファ溢れの可能性は残る。

そこでnum_threadsを2以上にする。

例えばnum_threadsが2で、2チャンクがキューに溜まっている場合、1スレッドが1チャンクを処理すれば0.5秒で送信できる。これであればバッファは溢れない。

BigQuery APIが遅い場合も考慮して、大きめの値にしておく。

buffer_chunk_records_limit   500        # BigQuery上限
buffer_chunk_limit           1000000    # BigQuery上限
buffer_queue_limit           5000       # 1GBくらい
flush_interval               1          # 暇な時間帯は1秒おき
try_flush_interval           0.05       # チャンクが溜まったら早めに送信
num_threads                  4          # HTTP POSTが遅いので複数スレッド
queued_chunk_flush_interval  0.01       # チャンクが溜まった場合は待ち時間短めで連続送信

エラーとリトライ

buffer_chunk_records_limitbuffer_chunk_limitをBigQuery側の制限を超えない値にしていても413 Request Too Largeが発生することがある(らしい)。

また、リクエストが正当であっても、一時的に500系のエラーも発生する。

fluentdのアウトプットプラグインはリトライ機能を備えているので、エラー時は送信がリトライされる。

insertId

リトライ時にレコードが重複してBigQueryに登録されるのを防ぐためにはinsertIdを使う。BigQueryはinsertIdを少なくとも1分間は覚えていて、ベストエフォートで重複を防いでくれる。

fluent-plugin-bigqueryはinsert_id_fieldで指定されたフィールドをinsertIdとしてBigQueryに送信するので、レコード中にユニークな値があるのであれば、そのフィールド名をinsert_id_fieldに設定する。

nginxやApacheのアクセスログだと難しいが、アプリケーションからレコードを送信する場合など自分でコントロール出来るのであればinsertIdを入れるようにしておくと良い。

この記事はQiitaの記事をエクスポートしたものです

Discussion