fizz buzz の i % 15 == 0 を実務で使った話


おそらく初学者の方が一度は思うであろう
Fizz BUss問題のi % 15 == 0 ってどんな時に使うの?
これを最近実務で使ったので紹介したいと思います

使った経緯

GitHubのAPIを使ったバッチ処理を作った際に利用しました。
利用方法としてはレート制限の回避です。
曲解して伝えると、外部APIを使う際に1分間に複数回同じAPIを叩くとエラーが起きます。(実際はかくAPIによって違う)
これを防ぐ際に使用いたしました。

解説

説明用に簡単ですが作りました。
octokitというgithubのAPI gemを使っていますが、今回の本筋ではないため特に説明しません。
詳細はgithubにあげてます
https://github.com/minty1202/qiita-octokit

require "bundler/setup"
require 'octokit'

class Search_issue

  def initialize(token)
    @client = Octokit::Client.new(access_token: token)
  end

  def done(repo_name)
    page_num = 1
    i = 1
    api_issues = fetch_issues(repo_name, page_num)
    while api_issues.count % 30 == 0
      sleep 60 if i % 10 == 0
      page_num += 1
      api_issues.concat(fetch_issues(repo_name, page_num))
    end
    puts api_issues
  end

  def fetch_issues(repo_name, page)
    options = {
      state: 'all',
      per_page: 30,
      page: page
    }
    @client.list_issues(repo_name, options)
  end
end

これはリポジトリにあるissueを検索するコードです。
注目して欲しいのが

while api_issues.count % 30 == 0

の部分と

sleep 60 if i % 10 == 0

それぞれ説明していきます

while api_issues.count % 30 == 0

これは検索個数が30こちょっきりだった場合、次の30件を取りに行くために記述してます。
githubのissueはページごとに管理されており、MAX100件一ページになってます。(デフォ30件)
ですのでリポジトリのissueを全て撮りたい場合、今回のコードですと30件以上のissueを取るためには少し工夫が必要です。
while文を使い、持ってきた数が30こ丁度の場合次の30個を拾いに行くようにしています。

sleep 60 if i % 10 == 0

API関係をつかうときにこの記述はよくつかうことになるのではないかと思います。
最初にお伝えしたようにAPIにはレート制限がありますgithubですと確か10〜30までレート制限ですので、仮に1リポジトリに1000件のissueがあった場合、このsleep 60 if i % 10 == 0がなかったら、300件を撮った時点でエラーが返ってきます。ですので、ループ処理をしている際に10回行うたびに1分の時間を空ける。というのが必要になるのです。

まとめ

かなりざっとでしたが、こんな感じに使いましたという紹介でした。
他にもつかう機会はあるかもしれないですし、私よりもスマートなレート制限の回避方法はあるかもしれません。
ただ、少しでも今の学習が面白くなればと思って書かせていただきました。