DIGGLE開発者ブログ

予実管理クラウドDIGGLEのプロダクトチームが、技術や製品開発について発信します

Rails+ApartmentにPostgreSQLのプロシージャを導入して多軸分析の集計速度を向上させた話 その3~Rails + PL/pgSQL編~

こんにちは。DIGGLEエンジニアのzakkyです。最近、長女、次女&三女(双子)がそれぞれ高校、中学校へ入学しました。可愛かったです。

いよいよレポート(多軸分析)の集計処理部分のパフォーマンス向上施策の第3弾となります。

前回までの記事の紹介

diggle.engineer diggle.engineer

今回のお題

今回はRails+ApartmentでRails Wayに乗っかりつつPL/pgSQLを導入した際のあれやこれやをご紹介します。plv8については、今回以上に大変だったので別記事として次回お話しする予定となっています。

書かないこと

以下の内容は含みませんので予めご了承ください

  • Rails、Apartmentの導入などの基礎的な部分
  • PostgreSQLのプロシージャに関する説明

どれくらい速くなったのか

前回も記載しましたが、以下が今回改善した集計処理部分のLatency推移となります。 リリース後には低下していることが見て取れるかと思います。

大体3倍速程度には改善することができました。

赤線の辺りで今回の改善版のリリースを行っています

Railsでプロシージャを呼び出すために具体的にどうしたのか

もう3回目ですので、早速本題へ行きます。 Railsからプロシージャを呼び出すためには、本当に色々なハードルがありました。

※プロシージャの導入に至った詳しい経緯を知りたい方は前回までの記事を参照いただけますと幸いです

プロシージャをRails Wayで管理する

大前提となります。今後のメンテナンス性を考え、あくまでRails Wayに乗っかった形でプロシージャを管理していく方法を考えます。

最終的な変更内容については今回やった内容のまとめに記載していますので、結果だけ知りたい方は最下部を参照ください。

プロシージャをRailsの管理下に置く

Railsガイドに記載されている通り、ダンプフォーマットをsqlへ変更することで解決できます。

railsguides.jp

publicテナントだけをダンプ対象にする

Apartmentを使っていてダンプフォーマットをsqlへ変更した場合、db:migrateを実行すると、structure.sqlに全テナント分のテーブルがダンプされてしまう問題が発生します。

こちらについてはダンプする対象をpublicにすることで回避できます。

db:resetを動くように修正する

schema_search_pathが指定されていてダンプフォーマットをsqlへ変更している場合、以下のissueにある通りdb:resetが動きません。解決策としては、issue中に記載のある通り、structure_dump_flagsを指定することで回避できます。

github.com

Apartmentの新規テナント作成時にプロシージャを適用する

以下のREADMEに記載されている通り、use_schemasとuse_sqlを適用することで新規テナント作成時にプロシージャが適用されます。

github.com

余談

上記READMEを読んでいると気になる一文があります。

this option doesn't use db/structure.sql, it creates SQL dump by executing pg_dump

ソースコードを追いかけると、確かにstructure.sqlを使わずに、pg_dumpをApartmentの中で実行しています。(次回以降にお話ししますが、ここの辺りで問題が発生してapartmentに対してPRを出すことになりました)

db:migrateでプロシージャを登録する

ここでようやくプロシージャをRailsで登録できるようになります。

シンプルに以下で実行可能です。

  def change
    execute <<~SQL
      CREATE PROCEDURE sample_procedure()
      AS $function$

      BEGIN
      -- 処理を書く
      END;

      $function$ LANGUAGE plpgsql;      
    SQL
  end

プロシージャの管理方針を考える

今回作成するプロシージャは弊社の根幹のロジックであり、今後のレポート拡充などやパフォーマンス改善で更に手を加えることが既に見えています。そして、改修の都度migrationを書いていては、どこにどのプロシージャがあるかを確認できず、管理が非常に煩雑になる未来が見えます。

上記などを考慮した結果、弊社ではプロシージャのソースコードを管理しやすいようにプロシージャ用のディレクトリを作成して、その中でプロシージャを管理するようにしました。

このようにmigrationを実行するイメージです。

def change
  execute File.read('db/procedure/sample_procedure.sql')
end

ただ、そうすると、migrationを書き忘れたりlocalのdb:migrateの実行タイミング次第では登録されるプロシージャの内容が個々人で変わったりなど、色々な危険があるのではないかと気付き、最終的には、db:migrateを実行する度にプロシージャの最新ソースを登録するようなrakeを別途作成することとしました。

lib/tasks/procedure.rake
def install_all_procedures
  Dir.glob(Rails.root.join('db/procedure/*.*')).each do |file|
    ActiveRecord::Base.connection.execute File.read(file)
  end
end

task 'db:procedure' => :environment do
  puts 'Re-install procedures..'
  install_all_procedures

  Apartment::Tenant.each do
    puts "At Tenant: #{Apartment::Tenant.current}"
    install_all_procedures
  end

  if ActiveRecord::Base.dump_schema_after_migration
    Rake::Task['db:schema:dump'].invoke
  end
end

Rake::Task['apartment:migrate'].enhance do
  Rake::Task['db:procedure'].invoke
end
rakeの中身の説明

簡単にrakeファイルの仕様を解説をします。

まず、Apartmentの仕様として、db:migrateを実行すると内部でapartment:migrateが実行されます。 今回の場合、apartmentのmigrationが終わった後にプロシージャの登録を行いたいので、Rake::Task['apartment:migrate'].enhanceとしています。

install_all_procedures周りの処理は全テナント(publicスキーマ含む)に対してdb/procedure/配下の全ファイルを実行していくだけなので割愛するとして、Rake::Task['db:schema:dump'].invokeについては、上記のプロシージャ登録をstructure.sqlの取得タイミングの後に実行しているため、再度dumpしてあげる必要があり、このような記述となっています。

結果として、db:migrateを実行するとdb/procedure/配下の全ファイルが常に実行されるようなrakeファイルとなっています。

つまり、db:migrateを行うたびに毎回procedure配下のファイルが実行されてしまうため、各プロシージャの先頭ではDROP-CREATEの形での記述をしてあげる必要があります。

DROP FUNCTION IF EXISTS sample_function;

CREATE FUNCTION sample_function()
--以下略

登録したプロシージャを呼び出す

プロシージャの呼び出し自体は非常に簡単です。

# procedure
ActiveRecord::Base.connection.execute('CALL sample_procedure()')

# function
ActiveRecord::Base.connection.execute('SELECT sample_function()')

※パラメータを渡す際にはsanitize_sqlなどを行う必要がありますので、ご注意ください。

今回やった内容のまとめ

以下のような形となりました。

application.rb
  class Application < Rails::Application
    config.active_record.schema_format = :sql
    config.active_record.dump_schemas = 'public'
    # MEMO: db:resetが動くように調整
    # NOTE: https://github.com/rails/rails/issues/38695
    ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = ['--clean', '--if-exists']
  end
apartment.rb
Apartment.configure do |config|
  config.use_schemas = true
  config.use_sql = true
end

We're hiring!

DIGGLEでは、必要に応じてライブラリのソースを読み解いて問題解決に挑む開発メンバーを募集しています!少しでも興味があれば、ぜひ下記採用サイトからエントリーください。

open.talentio.com

Meetyによるカジュアル面談も行っていますので、この記事の話をもっと聞きたい!という方がいらっしゃいましたら、お気軽にお声がけください。

meety.net

Rails+ApartmentにPostgreSQLのプロシージャを導入して多軸分析の集計速度を向上させた話 その2 ~プロシージャ導入編~

こんにちは。DIGGLEエンジニアのzakkyです。最近、長男が1歳になり、長女が中学卒業、二女&三女が小学校卒業とイベントが盛りだくさんでした。毎日家の中がにぎやかです。

さて、今回も前回から始まったレポート(多軸分析)の集計処理部分のパフォーマンス向上施策の第2弾となります。

前回までの記事の紹介

diggle.engineer

今回のお題

今回はPostgreSQLのプロシージャ導入時のあれやこれやをご紹介します。

書かないこと

以下の内容は含みませんので予めご了承ください

  • プロシージャの書き方などの基礎的な部分
  • PostgreSQLのパラメータを使ったチューニング方法

どれくらい速くなったのか

前回も記載しましたが、以下が今回改善した集計処理部分のLatency推移となります。 リリース後には低下していることが見て取れるかと思います。

大体3倍速程度には改善することができました。

f:id:zakky21:20220224115950p:plain
赤線の辺りで今回の改善版のリリースを行っています

実装方針を決める

前回のおさらいとなりますが、前回は「単純にSQLを発行するだけのような部分はPL/pgSQLで行い、計算式の値の算出のような複雑な処理はplv8を使うハイブリッド方式を採用する」所まで決定しました。

今回は、具体的にどの部分をPL/pgSQLで書いて、どの部分をplv8にするかを決めて実装していきます。

PL/pgSQLとplv8に振り分ける

実際に行う処理をいくつかに分解し、複雑度によってPL/pgSQLとplv8に振り分けていきます。

計算処理の流れを確認する

こちらも前回のおさらいとなりますが、改善箇所は「登録されているデータをレポート用に加工する」箇所の高速化となります。弊社のサービス上では大きく以下のような流れで処理が構成されています。

  1. 計算式のパース
  2. データの集計&集約
  3. 計算式部分の計算

それぞれの処理の複雑度などを考えて振り分けを行っていきます。

計算式のパース

jsのライブラリであるpegjsで生成したjsを使ってパース処理を行っていますので、複雑度云々ではなく、jsが実行可能な環境が必要となります。

plv8がnodeベースの言語となりますので、plv8で実装します。

データの集計&集約

単純にSQLによる集約ができれば良い部分となります。

シンプルにSQLを書けば良いので、PL/pgSQLで実装します。

計算式部分の計算

一番複雑な部分になります。それぞれの値の相互関係や四則演算などを表現できないといけません。また、今後計算式機能の拡充をしようとした際のメンテナンス性も重要になります。

PL/pgSQLでは荷が重いので、自由度が高いplv8で実装します。

最終的な振り分け結果

上述までの検討の結果、以下の様に振り分けを行いました。

  1. 計算式のパース ⇒ plv8
  2. データの集計&集約 ⇒ PL/pgSQL
  3. 計算式部分の計算 ⇒ plv8

プロシージャへの置き換え

方針も決まりましたので、それぞれの処理をプロシージャに置き換えて高速化を行っていきます。

計算式のパース

前述の通り、jsのライブラリであるpegjsで生成したjsを使っていますが、元々はrails上でjsを実行していました。そのため、お世辞にも処理が速いとは言えず、都度パースするのではなく、DB上にパース後の内容を保存しておくなど、極力パース処理が走らないような工夫を行っていました。

それが今回、pegjsで生成したjsをnodeベースであるplv8上で動かすことでかなり速度が向上しました。そのため、「パース処理の仕様を変えたから、全ての計算式をパースし直したい」のような時にも以下の様にSQL上からも呼び出すことで、今までは数分単位で掛かっていたものが1~2秒で終わるようになりました。

-- formula_tableの全ての計算式(formula)を再度パースしてparsed_jsonに格納する
UPDATE formula_table
SET parsed_json = parse_formula(formula);

※実際には計算式変更後の初回時のみパース⇒DBへ保存する仕様のため、最初の画像のLatencyに対する恩恵はほぼほぼ得られていません。

データの集計&集約の高速化

以前は同様の内容をRails側で取得⇒メモリ上に展開していたので、RDBとのI/Oや取得したデータの展開などのオーバーヘッドが掛かっていました。 今回はPL/pgSQLでSELECT-INSERTを用いてTEMPORARY TABLEへそのまま集計&集約しつつ投入する仕組みにしたため、オーバーヘッドを大幅にカットすることができ、大体1秒程度あればデータの集計ができるようになりました。

計算式部分の計算

今回の目玉です。

元々Railsで記載されていたものをplv8(node)へ置き換える作業となるのですが、試行錯誤の連続でした。

TEMPテーブルを活用する(失敗)

最初に実装した際には、計算式で参照する値がある度に、TEMPテーブルから都度参照する仕組みとしました。

理由としては、TEMPテーブルはオンメモリに展開されている筈で、TEMPテーブルへのSELECTは実体としてはRails上のMapなどに対する参照とコストが大差なく、そこまで大きくオーバーヘッドは掛からないだろう。という推測からでした。

しかし残念ながら結果としては、Rails時より速度低下する事態となってしまいました。

後で調べてみて分かったのですが、以下の記述があるように、どうやらTEMPテーブルもファイルへ保存しているようです。TEMPテーブルから取得したからいくらSELECTしても大丈夫!という訳ではなく、どこかしらでHDDへのI/Oが走ってしまい性能劣化へ繋がったのではないかと推測しています。

しかし一時的なリレーションでは、ファイル名はtBBB_FFFという形になります。

www.postgresql.jp

PostgreSQLのパラメータをチューニングする(失敗)

私よりDBに詳しい方が色々なサイトで纏めてくださっているので詳細は割愛しますが、以下のようなパラメータを調整してみました。

  • shared_buffer
  • temp_buffers
  • work_mem

色々な値を入れて試してみたのですが改善せず、今回は時間の都合で断念となりました😢

www.postgresql.jp

考察:plv8でSQL発行するのは良くないのではないか

上記の調査をしている中で気づいたことなのですが、体感でplv8では各種SQLの発行が遅いように感じました。 特に更新系の処理が顕著で、明らかに遅いと感じられる状況となりました。

後になって思い返してみると、計算式の計算が終わる度にTEMPテーブルへ計算結果をUPDATEする仕組みがあったため、UPDATEのコストが大きかった可能性もあります。(他の計算式から参照される可能性があるため、集計結果は保存しておく必要があるのです)

また、今回plv8で記述した内容を、頑張ってPL/pgSQLで書くようにすれば違った結果になった可能性があります。(ただし、メンテナンス性がかなり落ちるため、今回は採用しませんでした)

最終案:オンメモリに全展開する

最終的には、Railsでやっていたことと同じく、オンメモリに必要な情報を全て展開してから処理を行う。という形となりました。当たり前ですが、RailsでやっていたことをRDB上で実現しましたので速度の向上が見られました。

大まかな処理の流れとしては以下のようになります。

  • TEMPテーブルに対してSELECTを行い、集計結果をメモリ(Map)に展開する
  • 計算式で参照する値がある場合は、メモリ(Map)から参照する
  • 計算式の計算が終わったら、メモリ(Map)に結果を格納する

上記によって懸念されるのが、RDBのメモリが枯渇しないか。という点かと思います。

この問題に関しては、弊社では本番同等の環境&データを用いたパフォーマンス試験を実施する仕組みがありますので、リリース前にしっかりと本番想定の性能試験をすることが可能となっています。

そして、性能試験の結果としてはメモリ枯渇は発生せず問題なし。結果的にこちらが採用となりました。

まとめ

最終的に時間の都合で納得のいく形までは持っていけませんでしたが、3倍速程度には速くできたので、まぁ及第点かな。と。

ただ、計算式の計算部分のパフォーマンス改善や、PostgreSQLのパラメータチューニングなど、まだまだ改善の余地が残っていることは事実ですので、日を改めて再チャレンジをする予定です。

次回予告

「Rails+ApartmentでどうやってRails Wayに乗っかりつつプロシージャを管理するのか」という所を書きたいと思います。そちら側の方が個人的には大変だったので、ご期待いただければと思います。

本当は今回書きたかったのですが、長くなってしまった結果、泣く泣く分割となりましたので、来月までお待ちいただけますと幸いです。

We're hiring!

今あるものを更により良くするための方法を、我々と一緒に模索してくれる開発メンバーを募集しています!少しでも興味があれば、ぜひ下記採用サイトからエントリーください。

open.talentio.com

Meetyによるカジュアル面談も行っていますので、この記事の話をもっと聞きたい!という方がいらっしゃいましたら、お気軽にお声がけください。

meety.net

Rails+ApartmentにPostgreSQLのプロシージャを導入して多軸分析の集計速度を向上させた話 その1 ~技術選定編~

こんにちは。DIGGLEエンジニアのzakkyです。最近、長女の高校受験が終わって少し落ち着きました。

今回から何回かに分けてDIGGLEの肝となるレポート(多軸分析)の集計処理部分のパフォーマンス向上施策についてお話しします。

今回のお題

今回はPostgreSQLのプロシージャ導入に至るまでの技術選定部分をご紹介します。

レポート機能では何ができるのか

改善施策として何を行ったのかを語る前に、まずは背景として弊社のレポート機能を簡単に紹介させてください。

弊社のレポートは自由度が高く、大きく以下の様な機能があります。

  • 任意の計算式を作ることができる(Excelで計算式を作るようなイメージ)
  • 任意の軸を指定した分析ができる
  • 分析結果からドリルダウンができる

それぞれの機能の細かい内容までは掘り下げませんが、「サーバーへの負荷が凄いことになりそう」と感じていただければ幸いです。

diggle.jp

課題となっていたこと

ユーザーの状況

昨年あたりから、弊社営業・マーケ・CSの尽力で様々なお客様と契約いただき、それに伴い事業規模の大きなお客様にご利用いただく機会が増えてきました。

それ自体はとても嬉しいことなのですが、事業規模が大きいため、投入される分析用のデータ量も多くなり、取り扱うべきデータ量が以前と比べて飛躍的に増大する結果となりました。

結果として、よくある話ではあるのですが、パフォーマンス劣化が発生する事態となり、特に大量のデータを使って分析を行うレポート(多軸分析)では、処理時間が長くなってしまうという問題が発生していました。

ボトルネックとなっている箇所の洗い出し

弊社のサービスでは、レポート(多軸分析)を表示するまでの間に行う処理として大きく以下の3つがあります。

  1. 登録されているデータをレポート用に加工する
  2. レポート用に加工したデータをユーザーが指定した任意の軸・条件で取得する
  3. フロントエンド側で描画する

この中で、今回一番パフォーマンス低下が顕著だったのが「登録されているデータをレポート用に加工する」箇所となります。

他の2点に関しては、以前に改善施策を打っていたこともあり、パフォーマンス低下もそこまで顕著では無かったため、今回の改善対象とはしませんでした。

調査の際には、DatadogのAPMなどを使うことで、どこがボトルネックになるのかを視覚的に把握することができます。ご参考までにリンクを貼っておきます。

www.datadoghq.com

具体的にどこに問題があったのか

前述のDatadogのAPMや、サーバーのCPU/メモリなどを調査した結果、以下のような問題があることが分かりました。

  • RDBから取得したデータをRailsに取得するまでに以下のオーバーヘッドが掛かる
    • SQLの実行時間
    • ネットワークI/Oのオーバーヘッド(イントラネット内だとしてもデータ量が多いと問題になり得ます)
    • 取得した結果をRails上に展開するのにもオーバーヘッド
  • 扱うデータ量が増えるとメモリを圧迫し、CPU使用量が増える(スワップが起きていると推測)

なぜ問題が起こっているのか

弊社のサービスの特徴にも関係してくるのですが、前述の問題が起こる理由として以下のようなことがあります。

最初にも軽く触れましたが、弊社のサービスでは「任意の計算式を作ることができる」という機能があり、「登録されているデータをレポート用に加工する」際には、同時に計算式部分の値まで計算しておく必要があります。

そして、「任意の計算式を作ることができる」機能では、集計した結果を別の場所で参照し、更に別の場所で参照して・・・。といった表現が可能となります。

ここまで聞いただけで頭が痛くなってくるかと思いますが、つまり、集計する際は単純にSQLでgroup byするのではなく、それぞれの依存関係を見ながら計算していく必要があり、どうしても一度データをどこかに展開する必要が出てきます。

「だったら集計した結果だけ良い感じに残しておくようにすれば扱うデータ量自体は減るじゃないか!」と、思われるかもしれませんが、そちらも最初に触れた「分析結果からドリルダウンができる」ためには、一番深くまでドリルダウンした時の値まで保持しておく必要があり、簡単には取り扱うデータ量を減らすことができません。

今回行うべき事項は何なのか

ここまでで洗い出した問題点から、満たすべき要件と、改善すべき事項を導き出します。

満たすべき要件

  • 計算式の値を算出できる
  • ドリルダウンができる

改善すべき事項

  • 現在のI/Oと比べて同等か、もしくはそれ以上高速にRDBからデータを取得する
  • 大量のデータをメモリなどのI/Oが高速な場所へスワップが起こらないように負荷なく展開する

何を改善したのか

先ほどまでで課題の検討を行って何を行うべきかを導き出したので、ここからはどうやって実現するかを考えていきます。

今までの構成の延長で何とかならないか

今回の改善を行う前までは、「登録されているデータをレポート用に加工する」機能はRailsサーバー上で実行していました。

計算式の値を算出するためには、かなり複雑なロジックを記述する必要があり、バックエンドサーバーとして使っているRails上で実装するのが現実的である。という判断でした。

そのため、検討当初はRailsサーバー上で何とか改善できないかと検討もしたのですが、実は、高速化については私が入社した2019年当初より、継続的に検討&改善を行ってきた関係で、Rails上で行える改善はやり尽くしている感があり、これ以上の劇的な改善は難しいとの結論を出さざるをえませんでした。

そして結果として、Rails以外で解決する方法が無いかを検討することとなりました。

具体的にどうやって高速化するか

Rails以外にも裾野を広げて検討していく中で、大きく以下のような案が出ました。

  1. Rails以外の高速な言語でデータ集計用にサーバーを新規で立ち上げる
    • メリット 完全に独立した集計特化の構成を組める
    • デメリット どの言語を使うのかの選定が必要/劇的に改善するかはやってみないと分からない/導入までに時間が掛かる
  2. Redshift、BigQuery、Snowflakeなどを導入する
    • メリット 弊社が扱うデータ量であれば超高速に処理できる
    • デメリット RDBからデータを流すオーバーヘッドが掛かる
  3. RDB(PostgreSQL)上でRailsでやっていた内容を実行する
    • メリット 新しく何かを構築する必要が無い/同一サーバー上で処理が完結するのでネットワークI/Oをゼロにできる
    • デメリット PL/pgSQLを使って計算式の値を算出することは難しい

結果としては、上記のメリット/デメリットに加えて、開発工数、リスク、サービスとしての今後の見通しなど様々な要因を鑑みて、一番下の「RDB(PostgreSQL)上でRailsでやっていた内容を実行する」を選択することとなりました。

使用言語の策定

RDB(PostgreSQL)へ処理を移譲するという事で、真っ先に思いつくのがプロシージャ(PL/pgSQL)です。 ただ、前段でデメリットとして挙げた通り、計算式の値の算出のような複雑な処理をPL/pgSQLで書くのは、現実的ではありません。(プロシージャの開発経験がある方であれば、同意いただけるのではないでしょうか)

ということで、PostgreSQL内で実行可能な他の言語の調査を開始しました。

そんな中、nodeベースで記述できるplv8があることを知り、PL/pgSQLでは表現しづらい難しい部分はplv8で実現することにしました。

ただし、PL/pgSQLで書ける部分は極力書くことで速度は出したい。ということから、単純にSQLを発行するだけのような部分はPL/pgSQLで行い、計算式の値の算出のような複雑な処理はplv8を使うハイブリッド方式を採用しました。

最終的にどれくらい速くなったのか

今回プロシージャ化を行ってパフォーマンス改善を行いましたが、気になる改善結果は、というと・・・

以下が今回改善した集計処理部分のLatency推移となります。 リリース後には全体的に数値が低下していることが見て取れるかと思います。

大体3倍速程度には改善することができました。

f:id:zakky21:20220224115950p:plain
赤線の辺りで今回の改善版のリリースを行っています

次回予告

今回は技術選定部分のみにフォーカスを当てましたが、次回からは実際にプロシージャを使ってどのように改善したのかや、どうやってRails Wayに乗っかりつつプロシージャを管理したのか。といった細かい部分に踏み込んでいきたいと思います。

We're hiring!

今あるものを更により良くするための方法を、我々と一緒に模索してくれる開発メンバーを募集しています!少しでも興味があれば、ぜひ下記採用サイトからエントリーください。

open.talentio.com

Meetyによるカジュアル面談も行っていますので、この記事の話をもっと聞きたい!という方がいらっしゃいましたら、お気軽にお声がけください。

meety.net

DIGGLEを支えるbotのお仕事

こんにちは。DIGGLEのCTO 水上です。最近、生物分類技能検定の4級に合格しました。

さて、DIGGLEも今年からテックブログをはじめました。今回、第一弾として、DIGGLEを支えるslack botのお仕事についてご紹介します。

当然、普段は本プロダクトの機能追加や改修で手一杯であるわけですが、とはいえ単純作業に時間を使いたくもありません。サクッと実装したbotに単純作業を委ねられれば、生産性を高めることができます。

もちろんニーズのあるものや工数削減になるようなタスクでないと、大抵は使われない運命にあります。今回は、現在も良く使われているbotのタスクを紹介していきます。

まずは、kawauso君のご紹介

DIGGLEには、通称kawauso君というbotがいます。内実は、herokuに立てたhubotサーバとなっています。よくあるやつですね。

f:id:norainu234:20220124184808p:plain

kawausoのお仕事その1【リリース編】

リリースと言っても、実際のデプロイはGitHub Actionsがやっています。DIGGLEのデプロイは、GitHubに環境ごとのブランチ( productionstaging )が存在して、それらのブランチに対してマージが行われることをトリガーに、GitHub Actionsがデプロイを行う仕組みになっています。

そのため基本的には、リリース時は リリースしたいブランチ環境 のプルリクエスト(リリースPR)を作成してマージすることによって、リリースを開始できます。

slack経由でのリリースPR作成

リリース時はPR作成してマージするだけで良いとは言うものの、そのリリースが含むリリース内容をmasterとproductionの差分から自動的に抽出してくれると、何が出るのか分かりやすいです。 github-pr-release というライブラリを使うと、PR作成と、リリース対象のリストアップまですべてやってくるような、hubotのコマンドを簡単に実装できます。

f:id:norainu234:20220125125711p:plain
kawausoへのリリースブランチ作成依頼

f:id:norainu234:20220124214709p:plain
作成されたPR. リリースされるPRの内容が自動で入ります

顧客デモ中などにおけるリリースのブロック

CSやセールスがデモ等、重要な打ち合わせの際には、UIが変わってしまったり、変なトラブルが発生すると困ることがあります。DIGGLEでは、こうしたリリースを避けるべき時間帯に、開発以外のチームが開発側にリリースを避けるようにお願いする場合は、ReleaseBlockというGoogleCalendarに予定を作る運用になっています。

このカレンダーの予定がある場合には、上記のリリースPR作成を試みても、kawausoは受け付けないようになっています。これによって、依頼があるにも関わらず誤ってリリースしてしまうといったミスを防ぐことができます。もちろんこれは、あくまでslack経由でのリリースを防止できるだけの機能なので、クリティカルなバグ修正が必要な場合は、GitHub上で直接PRを作成すれば運用上は問題ありません。

f:id:norainu234:20220125125910p:plain
ブロック中にリリースしようとするとkawausoに怒られます

kawausoのお仕事その2【レビュー周り】

レビュワーのランダムなアサイン

レビュワー指名が偏ってしまうと、誰かに過剰な負荷がかかってしまうリスクがあります。またリポジトリの規模にもよりますが、今のDIGGLEぐらいのチームにおいては、仕様や実装理解の属人性を減らすことは重要です。そこで、レビュワーをbotにランダムにアサインさせる仕組みがあります。

kawauso君はGitHub上の「レビュー待ち」ラベルが貼られたタイミングを契機に、Webhook経由でランダムにレビュー依頼をしてくれます。レビュワー候補はGitHubのteamで管理しているため、新たにメンバーが入社した場合、レビュワーとして入って問題ないと思ったタイミングでteamに追加することでランダムにアサインする候補に入ります。

また、DIGGLEでは利用しているOSSライブラリのバージョンアップを自動でPRにしてくれるdepfuというサービスを利用しており、このバージョンアップPRが作成された場合にも、自動的にレビュワーが割り当てられるようになっています。

f:id:norainu234:20220124231209p:plain
レビュー待ちラベルをつけるとkawausoがアサインしてくれる

レビュー依頼されているPRのslack上での確認

自分に依頼されたPRの確認というのは、GitHub上でのフィルタでも見えるのですが、依頼から何時間経過したのかなど詳細が見えづらく、やや使いにくい部分があります。そこで、slack上で対話的に確認できるようになっています。下記のように、レビュー依頼から何時間経ったのかが出るので、優先するべきレビューを把握することが出来るようになっています。 (この機能は、非常によく使われています)

f:id:norainu234:20220124231634p:plain
kawauso check reviewsでレビュー待ち一覧を出してくれる

まとめ

以上、DIGGLEでよく使われているbotタスクを紹介しました。

これらの実装は公開するレベルには至っておりませんが、もしニーズがあれば @mizukami234 までDMください。

We're hiring!

時にはbotも駆使しつつ、生産性を高めながら開発を進める開発メンバーを募集しています!少しでも興味があれば、ぜひ下記採用サイトからエントリーください。

open.talentio.com