はじめに
Google Sheets APIなどは、一定時間あたりのAPI実行回数に制限があって、
ちょっとした処理でもすぐにその制限を超えてしまうことがあります。
そんな場面で使えるRubyによる再処理についてお伝えしたいと思います。
この記事ではGoogle Sheets APIの使い方については説明いたしませんので、
Google Sheets APIの詳しい使い方を知りたい場合は他の記事をご参照ください。
Google Sheets API ついて
Googleドライブ上のスプレッドシートを操作するためのGoogleが提供しているAPIです。
スプレッドシートのセルの値を読み取ったり、セルに値を書き込んだりすることができます。
RubyからはGoogle公式のGemを使うと便利に操作することができます。
https://github.com/googleapis/google-api-ruby-client
gem 'google-api-client'
Google Sheets APIの回数制限について
Google Sheets APIは、下記URLページに記載されているように、API呼び出しに回数制限が設けられています。
https://developers.google.com/sheets/api/limits
Googleに対して割り当ての増加リクエストを行うことができますが、トラフィック量が多いと料金が発生する可能性があるようです。
Google Sheets APIの例外
Google Sheets APIを短時間に集中して実行すると
「Quota exceeded … 」といった例外が発生することがあります。
前掲の表に記載されている制限を超えた場合に発生する例外です。
1分間、時間を空けて再度APIを実行すると今度はエラーが発生せずにAPIを実行できます。
Rubyによる再処理の方法
- 「begin rescue retry」で記述する
- メソッドやクラスに切り出す
- Gemを使う(Retryable)
1.「begin rescue retry」で記述する
サンプルコード
SLEEP_SEC = 60
MAX_RETRY = 3
retry_count = 0
begin
# Google Sheets API の呼び出し処理
rescue => e
if retry_count >= MAX_RETRY
# リトライ回数オーバー
raise e
end
retry_count += 1
# リトライ待機中
sleep SLEEP_SEC
retry
end
(すべての例外をrescueしているのは目をつむってもらうとして)
Google Sheets API の呼び出し処理(1)
他の処理(1)
Google Sheets API の呼び出し処理(2)
他の処理(2)
Google Sheets API の呼び出し処理(3)
他の処理(3)
といったAPI呼び出しを行っている場合、そのまま再処理を記述すると次のようになります。
SLEEP_SEC = 60
MAX_RETRY = 3
begin
# Google Sheets API の呼び出し処理(1)
rescue => e
if retry_count >= MAX_RETRY
# リトライ回数オーバー
raise e
end
retry_count += 1
# リトライ待機中
sleep SLEEP_SEC
retry
end
# 他の処理(1)
begin
# Google Sheets API の呼び出し処理(2)
rescue => e
if retry_count >= MAX_RETRY
# リトライ回数オーバー
raise e
end
retry_count += 1
# リトライ待機中
sleep SLEEP_SEC
retry
end
# 他の処理(2)
begin
# Google Sheets API の呼び出し処理(3)
rescue => e
if retry_count >= MAX_RETRY
# リトライ回数オーバー
raise e
end
retry_count += 1
# リトライ待機中
sleep SLEEP_SEC
retry
end
# 他の処理(3)
Google Sheets APIではどの処理で回数制限がオーバーするかわからないので、
リトライさせたい処理が多いとコードが煩雑になります。
2. メソッドやクラスに切り出す
class RescueRetryUtil
def initialize(sleep_sec:, retry_count:)
@sleep_sec = sleep_sec
@retry_count = retry_count
end
def call(&block)
retry_count = 0
begin
block.call
rescue => e
if retry_count >= @retry_count
# リトライ回数オーバー
raise e
end
retry_count += 1
# リトライ待機中
sleep @sleep_sec
retry
end
end
end
# 呼び出し例
util = RescueRetryUtil.new(sleep_sec: 60, retry_count: 3)
util.call do
# Google Sheets API の呼び出し処理(1)
end
# 他の処理(1)
util.call do
# Google Sheets API の呼び出し処理(2)
end
# 他の処理(2)
util.call do
# Google Sheets API の呼び出し処理(3)
end
# 他の処理(3)
この例ではクラスにしていますが、状況に応じてコントローラーのメソッドなどに切り出しても良いでしょう。
3. Gemを使う(Retryable)
Retryableなどの公開されているGemを使うという手もあります。
https://github.com/nfedyashev/retryable
最後に
Gemがあるのでそちらを使ってサクッと作るのも良いですが、
すごい高機能なので使いこなすのが大変かもしれません。
自分で再処理のコードを書くと、用途に応じてコードを追記することもできます。
例えば、リトライした場合はログに状況を書き出すとか。
(Retryableでも可能ですがひと手間かかります)
P.S.
リトライ回数は、再処理の回数を表すのか、トライの回数を表すのか、どちらが一般的なのでしょう?