【銀座Rails#28 LT登壇】「モチベーションクラウドを支える非同期処理の変遷」

###はじめに 【銀座Rails】とは？

銀座Railsは、リンクアンドモチベーションがスポンサーをさせて頂いている地域Rubyコミュニティの一つです。リンクアンドモチベーションでは、技術コミュニティの支援を通じて、エンジニアのコミュニティ形成やコミュニケーションの活性化、及びエンジニアの技術力向上に貢献したいと考えています。今回は[銀座Rails#28](https://ginza-rails.connpass.com/event/196658/ "銀座Rails#28")に登壇したテックリード江上のスポンサートーク、伊藤のLTをレポートします！


##江上 「従業員のエンゲージメントで、良い会社の定義を変える」

>__江上 真人__　
__株式会社リンクアンドモチベーション__
__テックリード__


私たちは、技術コミュニティ「銀座Rails」が始まった頃からスポンサーとしてご支援しています。今回、スポンサートークという貴重な機会をいただきましたので、普段私たちがどのような想いで「[モチベーションクラウド](https://www.motivation-cloud.com/ "モチベーションクラウド")」を開発しているかを中心にご紹介できればと思います。

リンクアンドモチベーションは、__「良い会社の定義を変える」というミッション__を掲げています。これまで、良い会社とは、P/LやB/Sなど事業面の数字により評価されていました。それゆえ、企業によっては組織面が蔑ろにされることもあり、働く個人が人間関係や組織問題に苦しむことも多く見受けられます。
私たちは、良い会社の定義を事業面のみが良い会社だけではなく、組織面も良い会社が評価される社会をつくっていきたいと思います。

組織面を定量的に評価するために、弊社はモチベーションクラウドという組織診断プロダクトを2016年にリリースし、2019年には診断結果をもとに、組織を改善するためのプロダクトを2つリリースしています。
組織診断をおこなうプロダクト「[モチベーションクラウド](https://www.motivation-cloud.com/ "モチベーションクラウド")」は、従業員の方にアンケートを実施し、組織のエンゲージメント(※1)状態を診断します。__診断されたエンゲージメントの数値や組織課題をもとに改善するプロダクト__が、「[コミュニケーションクラウド](https://www.motivation-cloud.com/communication-cloud/ "コミュニケーションクラウド")」と「[チームワーククラウド](https://www.motivation-cloud.com/teamwork-cloud/ "チームワーククラウド")」になります。現在、モチベーションクラウドシリーズは順調に成長しており、月間2億程度の売上推移になります。
※1)エンゲージメント：会社と従業員の相思相愛度合い


👇 __スポンサーからのお知らせ__
リンクアンドモチベーションでは、開発組織の内製化に踏み切った2018年から、毎年[Qiita Advent Calendar](https://qiita.com/advent-calendar)に参加しています。2018年は惜しくも__企業ランキング4位でしたが、2019年/2020年は2年連続で全体ランキング1位を獲得__しました👏また、個人で__Qiita賞を獲得するエンジニア__も現れたりと、AdventCalenderは社内で盛り上がる毎年恒例のイベントになっています！


🎄 __Qiita AdventCalender バックナンバー__
[2018年 リンクアンドモチベーションシリーズAdventCalender](https://qiita.com/advent-calendar/2018/ranking/subscriptions/categories/company)
[2019年 リンクアンドモチベーションシリーズAdventCalender](https://qiita.com/advent-calendar/2019/mcs)
[2020年 リンクアンドモチベーションシリーズAdventCalender](https://qiita.com/advent-calendar/2020/mcs)
皆さん、良い記事があれば是非いいねくださいね！

##伊藤「モチベーションクラウドを支える非同期処理の変遷」
> __伊藤 遼__　
__株式会社リンクアンドモチベーション__
__アプリケーションエンジニア__

### 課題1：処理負荷の増大
本日は、モチベーションクラウドの非同期処理を刷新したお話をしたいと思います。ありがたいことに弊社のプロダクトは順調に成長していますが、一方で、処理負荷の増大と同時処理数の増加という2つの課題が発生していました。

スライドに「鳴り響くサーバアラート」と書きましたが、当社はバッチサーバ1台でcronを回し非同期処理を行っていました。最初はキャパシティを超えることはありませんでしたが、導入企業の規模が大きくなるにつれて、処理負荷が増大するようになりました。1つ1つの処理が肥大化するため、徐々にサーバの限界を超えていきます。

もともとの処理では、単一のサーバで大量のcronが登録されていました。当時は毎分20を超えるcronにさらに1時間ごとに10を超えるRakeタスクが起動しており、cronファイルがびっちり書かれている状態でした。今後は、可用性を高めてスケーラブルな構成にしていく必要があります。

###課題2：同時処理数の増加
お客さまから「集計が終わらない」という声をいただきました。法人向けのSaaSでは企業ごとに処理するため、導入企業数が増加するにつれて、待ち時間が長くなってしまいます。

具体的には、お客さまのメール送信やレポート集計といった作業を1社ずつ実行していました。シンプルでわかりやすいコードなのですが、導入企業数が増加すると、ループの数が上がったり、メール送信やレポート集計の処理が重くなったりすることで、後続の処理が遅延する問題が起きつつありました。さらに、例外発生時にも後続の処理が遅れる、もしくは処理されない問題が発生していました。今後は、企業ごとに処理を並列化する必要があります。

###非同期処理ができるgemとアーキテクチャを採用
この2つの課題を解決するために、非同期処理ができるgemとアーキテクチャを採用しました。表に記載されているものは候補として挙がっていたgemです。「sidekiq」や「resque」はGitHub（ギットハブ）でも人気で、使われている方も多いと思いますが、キューをメモリにのせるので、キューが消失した場合の対応で懸念があったため、今回は保留としました。

DBでキューを管理する「delayed_job」も人気がありますが、弊社であまり運用実績がないのと負荷耐性を重視していたため、今回は運用実績のあった「active elastic job」、つまりSQSとElastic Beanstalkのworkerを使う構成にしました。最終的には、もともと「Amazon EC2」が1台立っていたところを、間にSQSを挟み「AWS Elastic Beanstalk」のworkerをオートスケールできるような形でアーキテクチャの構成を変更しました。


###「active elastic job」の運用で苦労した点
ここからは「active elastic job」の運用で苦労した点についてお話しします。一つ目が重複配信されてしまうことです。SQSは必ず1回の配信を保証してくれますが、重複配信される可能性もあります。つまり、「処理Aをしてね」とSQSにメッセージを投げるとworkerには処理の依頼が2回きてしまう可能性があります。

ただ一方で、もともとの処理プロセスは冪等性（べきとうせい）が担保されていない、つまり重複実行されているとデータの不整合が発生したり、もともと1つのタスクしか動かないように設計していたので、重複実行されないように実装する必要が出てきました。

二つ目が、エラー発生時のリトライ処理の実装です。SQSはvisibility timeoutやDead letter queueなどの失敗時の挙動を持っていますが、もともとの処理では、お客様にメールを送る機能と、社内で使うレポートでは重要度が変わるため、処理時間がばらばらでした。

そのため、5回リトライしたら通知する「処理A」と、50回リトライしたら通知する「処理B」と、処理ごとにリトライ待機時間や回数制限を定義する必要がありました。

三つ目は、もともとの処理だと、依存関係のあるタスクが多く、処理の実行可能条件が複雑に書かれていたことです。つまり実行条件の処理がバラバラになっていて、たとえば処理Aは「集計処理が完了していたら動いてほしい」、処理Bは「一括の処理が完了していたら動いてほしい」、それ以外は「もう1回リトライしてね」というのを自分たちで実装しました。そのため、処理ごとに実行可能かを定義し、それをさらにリトライさせるところまで作りこまなければいけませんでした。


###待ち時間が大幅に減少。今後の開発生産性にもつながった
このあたりの苦難を乗り越え、無事リリースができました。結果、これまでのような待ち時間を減少させることができました。また、導入社数も増えているため、負荷が集中するタイミングもありますが、そこに対しても自動で対応できるようになりました。モチベーションクラウド過去最大社数の同時サーベイ開始という大規模なイベントもありましたが、こちらも問題なく乗り越えることができました。

もともと私は開発チームの中でデッドコードの整理を推進していました。しかし、この経験を通して、複数の責任を持っていた大きい処理を適切に分割できたり、非同期処理をするだけのテーブルを削除したりと、今後の開発生産性にも寄与できたと感じています。今後はすべての非同期処理を刷新していき、cronをやめて新しいジョブスケジューラーの導入し、同時にECS化も進めていこうと計画しています。

👇 登壇資料はSpeakerdeckにて公開中
[__モチベーションクラウドを支える非同期処理の変遷__](https://speakerdeck.com/lmi/motibesiyonkuraudowozhi-erufei-tong-qi-chu-li-falsebian-qian)