シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

RubyKaigi 2023 に参加してきました(@t___yokoyama) 【前編】

初めまして。シンクロ・フード開発部の横山朋玖です。

twitter.com

今日の記事は、RubyKaigi 2023に参加した横山のRubyKaigi 2023の参加レポートになります。
なお、こちらの記事は前編になります。前編では、RubyKaigiとは何か、RubyKaigiに参加した動機、RubyKaigi 2023で印象に残ったセッションや、技術的に刺激を受けたことなどをまとめられればと思っております。
そして後編は、前編には書ききれなかったRubyKaigi 2023での多くの出会い、出会いを通して学んだこと、得られた経験、そしてRubyKaigiの素晴らしさなどをありのままに書き記したレポートになります。よろしければ後編もご覧ください。

自己紹介

Ruby(Rails)を使って弊社のWebサービスを開発して一年半目の横山朋玖です。 弊社のサービスである求人飲食店ドットコムの開発を行っています。

また、趣味で英会話レッスンにも一年ほど通っております。

RubyKaigiとは?

RubyKaigi 2023の入り口から見た景色。Matsumoto! RubyKaigi 2023という文字とともに我々を素敵な三日間の旅へと誘う

RubyKaigiとは日本で毎年開催されている、Rubyに関する技術イベントのことです。ここ数年はパンデミックの影響でオンライン開催が続いていたようですが、RubyKaigi 2022からは現地での開催が再開しました。

そしてRubyKaigi 2023には、Rubyを利用している開発者をはじめ、三日間で約1500人ほどにものぼる多くの方が世界中から日本に集まりました。

RubyKaigiは、Ruby開発者同士の繋がりをより強め、コミュニティを活性化させるために年次で開催されるイベントです。
そのため、会場にはRuby開発者が馴染みのあるRubyネタがあちらこちらに散りばめられており、ネタには思わず笑ってしまうこともありました。

なお、これを読んでくださっている方の中の多くは、RubyKaigiをあまり知らない方がほとんどかと思います(実際私も去年まで知りませんでした)。
そこで、会場の雰囲気はこんな感じ!というのをいくつかの写真を通じ、簡単に共有できればと思います!

まずはこのイベントの醍醐味である、有名開発者によるセッションのスケジュールになります。
RubyKaigiでは、Rubyというプログラミング言語自体*1の開発者(この開発者のことを、Rubyコミッタと呼びます)や、Rubyコミュニティに大きく貢献している開発者などによるセッションが多く開かれます。
なんと今回のRubyKaigiでは同時に三つのセッションが並列で開催されていました

RubyKaigi 2023 スケジュール

以下の画像は、私の尊敬する開発者の一人であるSamuel Williams氏による発表の雰囲気になります。
彼は、HTTPというWebプロトコルにまつわる問題提起、そしてその問題を解消するためにご自身が作成されたプログラムをセッションで発表されていました(詳しい内容については、本記事中のセッションについての紹介セクションをご覧ください)。

Samuel Williamsによる発表

さらに、会場では多くのスポンサーがブースを開いていました。
ブースにて、スポンサーによるRubyKaigiオリジナルグッズの配布や、二日目にはブースを巡ってスタンプを集める「スタンプラリー」も開催されていたりと、スポンサー企業と関わることのできる機会が沢山ありました!
セッションが開催中の時間帯でもブースにはいつも人がいましたので、セッションが開催している時に敢えてブースに遊びに行ったり(もちろんガラガラ)、朝早くから会場に出向きブースに遊びに行ったりということもできました。
混んでない時間にブースに立ち寄ると、もちろんブースの運営開発者とより長い時間会話ができる嬉しさもありますが、それ以上に「(スタンプラリーやグッズを求める)1RubyKaigiの参加者」としてではなく「技術に興味のある1開発者」としてより深く交流できる嬉しさがありました。
私の場合、スポンサー企業に勤める開発者の方と技術的なお話をさせていただき、交流させていただきました。
例えば、弊社のリモートワーク上の課題を他社さんに共有してみたのですが、他社さんも同じ悩みを抱えていることがわかったり、逆に「こういうふうに弊社は取り組んでいます」という風なアドバイスをしてくださったりしました。
弊社にとって、また、私個人にとっても有益な情報をブースの交流を通してたくさん知ることができました。

スポンサー一覧

さて。前述したように、スポンサー企業が当日配布されているRubyKaigiオリジナルグッズも紹介できればと思います。
私の一番のお気に入りグッズは、Findyさんにいただいた「なんもわからん付箋」でした(笑)。この付箋は裏表別の言葉が書かれており、表側には「完全に理解した」という言葉が書かれています。 ところで、実はこれらの言葉はエンジニア界隈の語録的なものです。「完全に理解した」という言葉はエンジニアがある分野について勉強を進め、少しその分野に詳しくなってきた時に良く口にします。分野全体の理解度が20% ~ 30%程度の時にこのように口にしている傾向が強いです。ソースは私です。

そして、分野の勉強を進めていくにつれ、当然理解できないこともまた増えていきます。壁にぶつかる度に、着実に理解度は「完全に理解した」時よりも増えているのに、自分の自信度は急降下していきます。そんな時エンジニアは「なんもわからん」と口に出し、自分の辛さを言葉で表現するのです。

この付箋はまさに、そういった「エンジニアあるある」を付箋として表現しています。私にとってとてもキャッチーな言葉に魅了され、愛用しています。

なんもわからん付箋

このように、三日間を通じて多くの開発者や企業からRubyコミュニティを盛り上げる催し物がたくさん行われました。
楽しすぎたあまり、ブースの様子を撮り忘れてしまいありのままの様子をお伝えできないのが少し残念ですが、これで少しでもRubyKaigiの雰囲気が読者の皆様に伝われば嬉しいです。

そして、なんとこのイベントには2018年、弊社から二人もの方が参加されたこともあります!
よければそちらの記事もご覧ください。

RubyKaigi 2018 に参加してきました (@fohte) - シンクロ・フード エンジニアブログ RubyKaigi 2018 に参加してきました (@n0_h0) - シンクロ・フード エンジニアブログ

なぜRubyKaigiに参加したのか?

Rubyは現在、多くの企業によってWebアプリケーションを作成するのに使われています。
そして、弊社も例外ではありません。弊社では、私の開発担当している求人飲食店ドットコムをはじめとする提供サービスの多くがRubyによって開発されています!

そしてRubyKaigi 2023では、前述したように世界中から約1500人ほどのRuby開発者が集まります。その中には日本の企業で働いている方、海外の企業で働いている方、プログラミング言語「Ruby」の生みの親である「まつもと ゆきひろ」さんをはじめとしたRubyの言語自体を作成している方、著名なRuby開発者なども参加されました。

会場の規模感や雰囲気は弊社エンジニアブログの過去の記事を見たりRubyKaigiの概要を調べたものの、正直あまりわかっていませんでした。
しかし、少なくとも私は「世界中から素晴らしい技術者が大勢集うKaigiである」ことは理解しておりました。そのため、私がRubyKaigiに参加した理由は、

RubyKaigi 2023に参加し、世界中の多くの人からできるだけ多くの、色々な種類のインスピレーション(モチベーション)を受けたい

からでした。

技術的なモチベーション、インスピレーションだけではなく、勉強に対するモチベーション、海外の人とお会いし英語に対するモチベーションなど、私が今の人生に必要だと思う全ての種類のモチベーション、インスピレーションが得れると信じ、私はRubyKaigiに参加しました。

そして結果的に、今回RubyKaigiに初めて参加し、私は日本人の中で最もRubyKaigiを楽しみ、一番多くの刺激を得られたと思えるほど、多くの経験や出会いをさせていただきました。そして、私の人生に今必要な全てのインスピーレーションをRubyKaigiにて得られたと心から感じています。

この記事では人との出会い、学んだ教訓、そして、どんな素敵な経験をRubyKaigiでしたのか?といったことは紹介しません
そのような内容は【後編】の記事にてありのままを書きました。気になった方は、ぜひそちらの記事をご覧ください。

セッションの紹介

Make Regexp#match Much Faster

speakerdeck.com

この発表は、正規表現のRubyコミッタであるHiroya Fujinamiさんによる発表です。

正規表現にはとても強い力があります。 例えば「"01 + 01 = 10"という文字列が二進数の足し算であるか?」を判定できたり、brainf**kインタプリタをRubyの正規表現で開発された方もいるように、とても強力です。

shinh.hatenablog.com

正規表現は強力な機能である一方で、大いなる力には大いなる責任が伴う(Fujinamiさんの発表中の言葉をお借りします)という言葉が示すように、正規表現には「弱点」があります。
この弱点は「ReDoS脆弱性」と呼ばれており、このReDoS脆弱性は現実世界でも問題になっています。例えば、あるサービスがReDoSが原因で27分間も使用不可能になってしまったり、RubyのGems(ライブラリ)で使われている正規表現にもReDoS脆弱性が存在していたりします。

この発表者のHiroya Fujinamiさんは、このReDoS脆弱性を引き起こしにくくするために、正規表現の性能を大幅に引き上げる修正をRuby 3.2.0で実装した方です。
彼は「正規表現の現状実装をどのように変えたらReDoS脆弱性を引き起こさないようにできるのか?」という問題に、「メモ化」と呼ばれる最適化手法を使い、正規表現の仕組みの最適化を行いました。
この発表では、開発時にどのようなことを考えていたのか、今後どんなことを実装していきたいか、という点についても発表をされていました。

bugs.ruby-lang.org

感動したこと

一番感動したことは、Rubyの全ての機能は、誰かが作っているということを実感したことです。
今まではRubyという仕組み・言語を「便利だから」使っていたに過ぎなかった私ですが、RubyKaigiに参加し彼の発表を聞いた後、Rubyの全ての機能は世界中の開発者が試行錯誤の末に成し遂げた「成果物」であることを実感せざるを得ませんでした。
実感すると同時に、正直、今まではRubyを「神から贈られたギフト」のように捉えていました。この発表を聞いた後意識はガラッと変わり、「Rubyは人が、人のために作るもの」であると捉え直すことができました。
私も近い将来、Rubyに新たな機能を実装できるような開発者になれたら嬉しいなと思いました。

そして次に、彼のスピーカーとしての講演での話し方です。
ReDoSについても全く知らない、そして当然正規表現についての実装の詳細が何も分からない私でもわかるように、丁寧な説明をしていただきました。

その中で私は、彼の「全ての聴衆に自分の気持ち、開発に対する向き合い方、そして、正規表現の面白さを伝えたい」という気持ちが伝わりましたし、私はその思いに至極感銘を覚えました。
彼の発表をお聞きし、彼が日々向き合っている「正規表現」のように、「自分が強く開発したい、貢献したい」と思える、私の技術的な適所を見つけたいと思いました。

Hiroya Fujinamiさん、素敵な発表をしていただき、本当にありがとうございました!

Fix SQL N+1 queries with RuboCop

speakerdeck.com

このセッションでは、ISUCONと呼ばれる「お題として与えられたWebシステムをどれだけ高速化できたかを競うコンテスト」に参加されているGo Sueyoshiさんが、「ISUCONで出題されるWebシステムの中の『長文SQL』からすぐにN+1問題が起きている箇所を見つけられるRuboCopを作りたい」という思いをきっかけに作成された、RuboCopルールについての紹介がされていました。

ISUCONにて出題されるSQLの例(スライドからの引用)
ISUCONにて出題されるSQLの例(スライドからの引用)

ISUCON本番時、上記のようなSQLからN+1問題を早く摘出するのは、急いでいるならなおさらとても難しいと私は思いますし、通常業務の中でも、SQLからN+1のような問題を見つけるのはとても時間がかかるものだと感じています。
その点、このリンターを使えば生SQL限定ですが、N+1問題を摘出することができるため、ISUCONにおいては勝率を伸ばすことに十分コミットするはずです。
そして、N+1問題を解消するRuboCopルール以外にも、いくつかのルールが紹介されていました(詳しくはスライドを参照してください)。

感動したこと

一番感動したことは、「Rubyでは、やろうと思えばなんでもできるんだな」 ということでした。
やりたいと思ったことを自分で開発できること以上に、開発者が嬉しいこと、誇れることはないと思います。私はそのように思っています。

そして実は、私が開発を始めたきっかけは「作りたいと思ったものが作れる人になりたい」からでした。 (最初に作成したツールは、まさに自分の欲望を満たすためだけに作ったプログラムでした。)

しかし、最近は「仕事のためのツール」としてプログラミングを使っているだけであり、自分の夢を叶えるために、自分が本当に作りたいと思ったものを実現するために、開発をできていませんでした。

そして、そんなことを考えることすらも忘れてしまっていました。

彼の発表を聞き、ずっと忘れていた、心の奥に閉じ込められていた開発者にとって一番大事な気持ちを思い出しました。
そして、発表を聞き続ければ続けるほど、どんどんとインスピレーションを感じ、まるでその気持ちを加速させるかのようにエネルギーが自分の周りからどんどんと集まってきました。
実はこの記事の執筆時点では実はもう気持ちがだんだんと失われはじめているのですが、やはり自分が感じた問題を自分の力でより良くできることはとても尊いことだと思いますし、今年何かを成し遂げたいと奮起できたので、忘れないうちにSlackのチャンネルは作成しました。

業務改善のアイデアをまとめるチャンネルを作成。暇な時に開発できそうな、深夜テンションで思いついたアイデアを投稿中

私もこれからRuby開発者としてのキャリアを積んでいくと思うのですが、その中で自分が少しでも感じた問題に敏感になり続け、直近で「Rubyを使い、自分が直面している問題の解決をしたい」と思いました。
Go Sueyoshiさん、本当に素敵な発表をありがとうございました!

Unleashing the Power of Asynchronous HTTP with Ruby

こちらのセッションの発表資料は以下のURLから入手可能ですので、皆さんも是非見てみてください!

github.com

このセッションでは、HTTPの30年の歴史の紹介、現状のRuby環境において、HTTP 2+をサポートするHTTPアダプター(HTTP ⇄ Rubyをサポートする機能)が少ないという問題の提起、その問題点を解決するためにHTTP/2をサポートするHTTPアダプター「Async::HTTP」の作成したことが発表されました。

そして将来的に(今年中に)Async::HTTPでHTTP/3のサポートも行うことが発表されました。

皆さんご存知のように、WebとHTTPは現在切り離すことができない関係になっています。そして、HTTPの初回リリースからは、今年約30年になります。
HTTPは最初こそはシンプルなものでしたが、時間が経つにつれどんどんと機能追加、改善がされていき、現在ではHTTP/3までバージョンアップがされております。

しかし、RubyではHTTP/2以上をサポートするHTTPアダプタ(HTTPを解釈できるプログラム)があまりないのです。つまり、バージョンアップしてパワーアップしたHTTPをRubyは十分に使いこなせていない ということになります。
それを解決しようとしているのが、本セッションの発表者であるSamuel Williams氏です。

一番感動したこと

彼のスライドには、素晴らしいアニメーションが多数埋め込まれています。そして、そのアニメーションはとても素晴らしく作用していました。特に、英語のネイティブではない私でも、彼のスライドを見るだけで伝えたいことが十分に理解できました。

彼の発表スライドのアニメーションの様子
彼の発表スライドのアニメーションの様子

アニメーションの素晴らしさ、そして、クリアで聞き取りやすい英語での発表にも感動しましたが、最も感動したのは、
彼の自信に満ち溢れた発表でした。

私はこの講演を聞く前、素晴らしい開発者とは単に「開発という領域において能力を発揮できる人」だと思っていましたが、この発表を聞き、
本当の素晴らしい開発者は、開発能力に長けているだけでなく、「自信に満ち溢れた、人としても素晴らしい人」であることを本当に感じました。

そしてセッションが終わった後、彼に質問をさせていただきました。彼は私一人に対して10分程にも渡って質問に真摯に回答してくださいました。
彼に回答をしていただいていた際、気付いたことがあります。
それは、素晴らしいライブラリ開発の裏には、私には想像できないほどの努力があるということです。

彼は発表の中で、「Async(彼の作ったライブラリ)はHTTP三十年の歴史の複雑さを10行のシンプルなコードで隠せるライブラリである」とおっしゃっていました。
ですが、ということはAsyncの開発の際、当然Samuel自身はHTTP三十年の歴史を知り、その複雑性に向き合う必要があります。さらに、他にもたくさんの課題があったはずなのです。
彼の発表では苦労話は一切出てきませんでしたが、私とマンツーマンで話してくださった時は開発時に辛かったことも教えてくださいました。

私もいつか彼のようになりたい。 本当にそう心から思わせてくれた、素敵な時間でした。本当に素敵な発表・素敵な時間をありがとうございました。

Samuelから講演後いただいたAsyncロゴのTシャツ
Samuelから講演後いただいたAsyncロゴのTシャツ

最後に

以上で【前編】は終了です。最後までお読みいただきありがとうございました! よければ【後編】も是非ご覧ください!

また、弊社ではRubyエンジニアを募集中です!詳しくは下記のサイトをご覧ください! www.synchro-food.co.jp

そして、今回のRubyKaigi 2023には旅費、交通費、会費などを全て経費でご負担していただきました。ありがとうございました。 弊社の福利厚生・制度の詳細は、以下のページをご覧ください。

www.synchro-food.co.jp

*1:プログラミング言語もまた、何か別のプログラミング言語のプログラムなのです

シンクロ・フードのエンジニアが書籍購入補助制度を利用して2022年度に購入した書籍ランキングTOP5を紹介します

こんにちは、シンクロ・フードの越森です。

今回は2023年度最初の記事と言うこともあり、シンクロ・フードのエンジニアが書籍購入補助制度を利用して2022年度(2022年4月〜2023年3月)に購入した書籍を紹介したいと思います。

シンクロ・フードの書籍購入補助制度とは?

始めにシンクロ・フードの書籍購入補助制度についてまとめると以下になります。

  • 制度概要
    • 技術書や業務で必要となる業務知識に関する書籍など、スキルアップにつながる書籍の購入を支援する制度
  • 支援内容
    • 1人当たり1年間で総額30,000円(税込)までの支援
    • 購入した書籍は個人所有(購入した書籍の金額分が課税対象)になる
  • 利用条件
    • 対象書籍は技術書と業務で必要となる業務知識に関する書籍
    • 冊数制限無し
    • 電子書籍での購入も可能

リモートワークを始めてから会社に保管する形式の書籍購入補助制度では使い勝手が悪くなったため、2021年9月にリニューアルして今の形式になりました。
メンバーにも非常に好評でスキルアップの一助になっていると思います。

2022年度の購入書籍

2022年度の1年間で購入された書籍数は218冊で、エンジニアは32名在籍していることから1人当たり6.8冊購入している計算になります。
大体2ヶ月に1冊は制度を利用して購入していると言うことですね。

2022年度の購入書籍ランキングTOP5

次に2022年度に購入された書籍のランキングTOP5は以下になります。

順位 書籍名 購入冊数
1位 プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則 15
2位 良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方 9
3位 研鑽Rubyプログラミング β版 6
4位 プロジェクトマネジメントの基本が全部わかる本 交渉・タスクマネジメント・計画立案から見積り・契約・要件定義・設計・テスト・保守改善まで 5
5位 りあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅲ.React応用編】 4
5位 プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで 4
5位 プログラマー脳 ~優れたプログラマーになるための認知科学に基づくアプローチ 4

1位の「プリンシプル オブ プログラミング3年目までに身につけたい一生役立つ101の原理原則」は会社で読書会を開催していたこともあり、多くのメンバーが購入しました。
シンクロ・フードでは若いメンバーが多いのですが、今後良いコードを書く上で重要な原理原則が多数紹介されており参考になったのではないかと思います。

越森は、principle 2.5として紹介されているSLAP(Single Level of Abstraction Principle) の項目が好きで、抽象レベルを揃えることで、優れた書籍のように読むことができると例えられているのが良いなと思います。

2位の「良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方」は、ITエンジニア本大賞2023技術書部門の大賞に選ばれるなど話題になりましたが、シンクロ・フードにおいてもランクインしています。
こちらの書籍は実践的な内容でテーマ毎に良いコード、悪いコードを元に解説されているので非常に分かりやすい内容になっています。

越森は昔、学生時代にテレホーダイで「Cプログラミング診断室」というWebで公開されていた書籍を読んでいたのですが、悪いコードを元に駄目な理由を解説されているところが似ているなと思って読んでいました。(歳がばれる...)

3位は、2023年4月にβが取れて新刊として販売された「研鑽Rubyプログラミング β版」がランクインしました。
シンクロ・フードではRuby on Railsを使って開発しているため、5位にも「プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで」がランクインしているようにRuby関連の書籍が多く購入されています。
入門書から中級者、上級者向けの書籍までバランス良く購入されているようでした。

最後に

今回の記事を書くために、購入されている書籍をまとめてみたのですが、購入書籍数が281冊に対して116種類と思いのほか多くの種類の書籍が購入されていて驚きました。
今年度はどのような書籍が読まれるのか楽しみにしたいと思います。


最後に、エンジニア募集のお知らせです!
シンクロ・フードでは、書籍購入補助制度に限らず今後もエンジニアが成長できる環境を整備していきたいと思っています。

また、飲食店ドットコムを始めとしたサービスの開発・改善も、どんどん進めております。
ご興味のある方は、以下の採用サイトからご連絡ください!
www.synchro-food.co.jp

OpenSearch の導入による検索システム改善のための認証・認可設計と負荷検証結果

SRE チームの @fohte です。普段は音楽ゲームを嗜んでいます。

シンクロ・フードでは、弊社が提供する求人飲食店ドットコムの求人検索にて、MySQL から OpenSearch を使った全文検索に切り替えました。
本記事では、その OpenSearch を導入する上で障壁となったことや、それらの解決策について紹介します。

なお、本記事では、インフラ面で課題となった以下のテーマについて扱います。

  • 認証・認可の設計
  • 負荷検証によるスペックや設定の検討

アプリケーション面での MySQL と OpenSearch のデータ同期や検索の工夫については、以下の記事をご覧ください。

tech.synchro-food.co.jp

OpenSearch とは

OpenSearch は、オープンソースの全文検索エンジンで、Elasticsearch からフォークしたものです。
MySQL をはじめとする RDB に比べると、大量のデータを高速かつ柔軟に検索できることが期待できます。

弊社のインフラ環境では AWS をメインで利用しており、OpenSearch のインフラとして Amazon OpenSearch Service を採用しました。

Amazon OpenSearch Service は AWS が提供するフルマネージドサービスです。
冗長化やリソースの最適化といったスケーリング、バージョン更新などによるデプロイの自動化ができ、運用コストを大幅に削減されることが期待できます。

OpenSearch 導入の背景

OpenSearch の導入以前の求人検索では、MySQL で検索条件を SQL に盛り込んで検索していました。
しかし、求人の掲載数が増えるにつれて、レスポンスタイムの増加や MySQL の負荷の増大が目立つようになりました。
そこで、大量のデータを高速かつ柔軟に検索できる全文検索エンジンを導入することとなり、OpenSearch を採用しました。

認証・認可の設計

シンクロ・フードの SRE チームでは、最小権限の原則に従って権限制御をするというポリシーを策定しています。
それに伴い、OpenSearch でも、以下のように権限を最小化することを設計のゴールとしました。

  • アプリケーションからは特定の index のみの read/write 権限を与える
  • アプリケーション開発者には、テスト環境でのみ権限を与え、本番環境では何も権限を与えない
  • インフラ運用者にはフル権限 (admin 権限) を与える

Amazon OpenSearch Service での認証・認可方式

Amazon OpenSearch Service のデフォルトでは、IAM によるドメインアクセスポリシーで制御します。

また、FGAC (Fine-grained access control、きめ細やかなアクセスコントロール) と呼ばれる設定 1 が存在します。
この設定を有効化すると、OpenSearch 自体に (IAM とは異なる) ロールを作成し、OpenSearch 側で認可できます。

FGAC を有効化しない場合、OpenSearch 側でロールを作成できず、ドメインアクセスポリシーだけで制御する必要があります。

しかし、ドメインアクセスポリシーは以下の制約があり、うまく権限制御が行えませんでした。

  • 後述するダッシュボードへのログインの許可・拒否は制御できるが、ログイン後の操作の権限制御ができない
  • OpenSearch を VPC 内に入れる時、ドメインポリシーで IP ベースでの制御ができない

これらを実現できる手段が FGAC であったため、今回は FGAC を有効化しています。

Cognito を利用してダッシュボードにログインする

Amazon OpenSearch Service では、Kibana からフォークされたダッシュボード (OpenSearch-Dashboards) が利用できます。

ダッシュボードでは、ブラウザー上で任意の API リクエストを発行できたり、クラスターや index の情報を閲覧・編集できます。

そのため、インフラ運用者にのみ編集権限を与え、アプリケーション開発者には閲覧権限のみを与える等の制御が必要です。

上述の通り、FGAC を有効化すると OpenSearch にロールを作成して認可ができます。
社内のユーザーがブラウザーからアクセスした際、そのロールにマッピングするため、Cognito を利用しています。

具体的な認証・認可のフローは以下の図の通りです。

flowchart TD

server_admins[インフラ運用者]
non_server_admins[アプリ開発者]
server_admins & non_server_admins -- "ダッシュボードへのアクセス" --> cognito_user_pool


subgraph AWS
  cognito_admin["Cognito インフラ運用者用ロール (IAM)"]
  cognito_default["Cognito アプリ開発者用ロール (IAM)"]

  subgraph opensearch-domain["OpenSearch ドメイン"]
    opensearch[(OpenSearch)]

    admin_opensearch_role["管理者ロール"]
  end

  subgraph Cognito
    cognito_user_pool[Cognito User Pool]
    cognito_id_pool[Cognito Identity Pool]

    cognito_id_pool --> is_admin{{インフラ運用者か?}} -- Yes --> cognito_admin
    is_admin -- No --> cognito_default
  end

   cognito_admin --> admin_opensearch_role -- "フル権限" --> opensearch
end

cognito_user_pool --> cognito_id_pool

cognito_default --> reject(("拒否"))

SigV4 署名を用いてアプリケーションから認証する

一方で、アプリケーションからの認証は、Cognito を介さずに SigV4 署名 を用いて直接 OpenSearch にアクセスしています。

弊社のアプリケーションは AWS の EC2 あるいは ECS で動かしており、IAM ロールでの認証が可能だったため、Cognito は介していません。

以下に示す図は、アプリケーションからの認証・認可のフローです。

flowchart TD

subgraph AWS
  subgraph ecs_task[ECS タスク]
    app[アプリケーション]
    ecs_task_role["ECS タスクロール (IAM)"]
    app --- ecs_task_role
  end

  subgraph opensearch-domain["OpenSearch ドメイン"]
    opensearch[(OpenSearch)]
    app_opensearch_role["アプリケーション用ロール<br>(OpenSearch)"]
  end
  ecs_task_role --> app_opensearch_role -- "限定された権限" --> opensearch
end

このように、IAM ロールを OpenSearch のロールにマッピングしています。
そのロールで、特定のリソースのみアプリケーションからアクセスできるように制御しています。

IAM 認証では、具体的には OpenSearch への API リクエスト時に SigV4 で署名しています。
今回は Ruby on Rails を利用しているアプリケーションのため、faraday_middleware-aws-sigv4 gem を使い、以下のように実装しています。

client = OpenSearch::Client.new(url: ENV['OPENSEARCH_URL']) do |f|
  f.request(
    :aws_sigv4,
    service: 'es',
    region: 'ap-northeast-1',

    # ECS を利用しているため ECS のタスクロールで認証する
    credentials_provider: Aws::ECSCredentials.new,
  )
end

負荷検証によるスペックの検討

弊社では OpenSearch の利用が初めてだったため、必要なリソース量が肌感覚として不明でした。
そこで負荷検証を行い、スペックを検討することにしました。

負荷検証の方法

今回の負荷検証の目的とゴールとして、大きく以下の 2 つの観点がありました。

  • OpenSearch 側の負荷が高くなることによって、アプリケーションのレスポンスタイムが劣化しない
  • index 更新時の限界性能を把握し、アプリケーション側で更新間隔の調整等を行う

これをもとに、実際の運用を想定した並列かつ高頻度なリクエストや、限界性能を確かめるリクエストで検証しました。

なお前提として、今回の検索システムでは index に格納するドキュメントは以下の仕様となっています。

  • 40,000 件のドキュメント
  • ドキュメントには検索対象のデータを入れている
    • 1 ドキュメントあたりのデータ量は平均 10 kB

検証内容は以下の通りです。

  • 検索リクエストの投入 (最大秒間 100 リクエスト x 10 秒間)
  • _bulk API による更新リクエストの投入 (最大 250 ドキュメント x 10 並列)

以上の検証により、大きなものとして 2 つの課題が見つかりました。

メモリ不足による GC でレスポンスが遅延する

検証中、リクエスト量が増加すると、1 秒前後で返していたレスポンスが瞬間的に 30 秒前後まで劣化することがありました。
このとき OpenSearch のログ 2 を確認すると、以下のように GC が走ったログがいくつか出力されていました。

[2022-12-07T09:15:13,391][WARN ][o.o.m.j.JvmGcMonitorService] [c69ee8a1e7009c34e215e585444667c8] [gc][young][1320808][1808] duration [3.2s], collections [1]__PATH__[4.1s], total [3.2s]__PATH__[12.4m], memory [952.3mb]->[504.8mb]__PATH__[2gb], all_pools {[young] [464mb]->[1mb]__PATH__[0b]}{[old] [485.3mb]->[499.8mb]__PATH__[2gb]}{[survivor] [3mb]->[4mb]__PATH__[0b]}
[2022-12-07T09:15:14,200][WARN ][o.o.m.j.JvmGcMonitorService] [c69ee8a1e7009c34e215e585444667c8] [gc][1320808] overhead, spent [3.2s] collecting in the last [4.1s]

これのログとリクエスト時刻のタイミングを照らし合わせると、この GC が起因してレスポンスタイムが遅延していると推測できました。
つまり、メモリ不足が根本的な要因と考えられるため、インスタンスタイプを変更し、メモリ量を増やしました。

再検証してみると GC の発生頻度が減り、この問題は解消できました。

余談: Auto-Tune による JVM ヒープサイズの変更の検討

今回はメモリの増強という手段を取りましたが、Amazon OpenSearch Service では Auto-Tune という機能があります。
これは、JVM ヒープサイズの変更をはじめとする、各種リソースに関する設定を自動的に変更する機能です。

docs.aws.amazon.com

例えば、Amazon OpenSearch Service での JVM に割り当てられているヒープサイズは、デフォルトでインスタンスのメモリの 50 % です。
Auto-Tune によりこのヒープサイズは変更できるため、GC の発生頻度を減らすことが期待できます。

ただし、ドキュメントに記載されている通り、JVM ヒープサイズの変更は無停止ではできません。
メンテナンスウィンドウとして設定した時間帯に Blue/Green デプロイメントされ、設定が変更されます。
つまり、負荷に応じた動的なヒープサイズ変更は期待できませんでした。

このことから、今回は Auto-Tune を使わず、単にメモリを増やす方針で GC の問題を解決することにしました。

refresh_interval は短くしないほうが良い

refresh_interval は、ドキュメントの追加や更新を反映するために、OpenSearch が自動的に refresh する間隔の設定です。

今回直面した課題では、サービスの要件として「極力早く反映してほしい」というものがありました。
OpenSearch のデフォルトでは refresh_interval は 1 秒となっており、今回も 1 秒で設定していたため、その要件自体は満たせます。
しかし、AWS のナレッジセンターでも、パフォーマンス向上のためには refresh_interval を 60 秒以上にすることを解決の一案として挙げています。

aws.amazon.com

実際、refresh_interval を 1 秒 or 60 秒に設定し検証したところ、60 秒のほうが全体的にレスポンスタイムが速く、極端に遅くなることも少ないことが分かりました。

refresh_interval を変えたときのレスポンスタイムの違い

以上の検証やナレッジセンターを参照すると、refresh_interval は短いほど負荷が高いため、極力長くしたほうが良いといえます。

ただ、今回は以下の理由により、refresh_interval は 1 秒のままにしています。

  • 前述したようにスペックを上げ、大きなパフォーマンス劣化に繋がらなくなったため
  • 要件として検索の反映ラグは少ないほうがよいため

今回の学びとして、検索までの反映のラグの少なさとレスポンスタイムの遅延のトレードオフを理解し、refresh_interval を調整する必要があるという知見を得ました。

最後に

今回は、OpenSearch を導入する上で障壁となった認証・認可の設計や、負荷検証時に発覚した課題とその解決策について紹介しました。

今回の OpenSearch 導入での知見を活かし、弊社が提供する求人検索以外のサービスでの検索システムにも OpenSearch を導入する予定です。
検索がより快適になるよう、SRE チームとアプリケーション開発チームが協同して導入を進めていきます。


  1. 実体はおそらく OpenSearch の security plugin と思われます。
  2. Amazon OpenSearch Service ではエラーログやスローログなどが見られますが、そのうちのエラーログ (ES_APPLICATION_LOGS) に出力されていました。

CI を Drone から GitHub Actions に移行しました

こんにちは、シンクロ・フードの大前です。

シンクロ・フードではCIにDroneを使用していましたが、2022年の11月ごろにGitHub Actionsへの移行を完了しました。
今回はその経緯やDroneとの比較についてお話します。

なぜ移行したのか

シンクロ・フードでは2018年ごろからDroneを使用していました。
導入時の記事があるので良かったらご覧ください。

tech.synchro-food.co.jp

その後、2020年9月ごろにGitHub Enterprise Server(以下GHES)でもGitHub Actionsが使えるようになりました。
(シンクロ・フードではGHESというオンプレ版を使っています。GitHub Enterprise Cloudと比べて機能追加が少し遅い特徴があります。)

Droneは課題を感じている部分もあり、GitHub Actionsの方が以下のような点で優れていたため移行しました。

GitHub Actionsのワークフロー構文の方が表現力が高い

CIを主に実行しているリポジトリはMonorepoで運用1しているので、各アプリケーションごとに自動テストを実行する必要がありました。
DroneではMatrix Buildsという記法でディレクトリごとに実行するワークフローを制御していましたが、GitHub Actionsではディレクトリごとにトリガーを設定することで読みやすい構造にできる点が魅力でした。

以下にDroneとGitHub Actionsのサンプルコードを記載します。(簡略化のため実際は必要な記述を省略していますがご了承ください)

Droneの場合

この後詳しく解説しますが、まずは全体のコードをご覧ください。

# 1. matrix(並列実行)を定義する
matrix:
  include:
    - SUB_REPOSITORY: ''
      RUBY_VERSION: 2.4.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application1
      RUBY_VERSION: 2.6.0
      APPLY_SEED: true
    - SUB_REPOSITORY: apps/rails-application2
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application3
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false

# 2. あとでYAMLのエイリアスとして使えるようにアンカーを定義する
references:
  if-root-matrix: &if-root-matrix
    matrix:
      SUB_REPOSITORY: ''
  if-app-matrix: &if-app-matrix
    matrix:
      exclude: SUB_REPOSITORY: ''

# 3. CIで実行するジョブを定義する
pipeline:
  run-tests:
    commands:
      - bundle exec rspec
    when:
      <<: *if-rails-app-matrix
  
  lint:
    commands:
      - yarn run lint
    when:
      << *if-root-matrix

  apply-seed-each-app:
    commands:
      - bundle exec rails db:seed
    when:
      matrix:
        APPLY_SEED: true

1. matrix(並列実行)を定義する

以下の部分ではMonorepo内のアプリケーションのディレクトリをSUB_REPOSITORYとして定義しています。
SUB_REPOSITORY: ''はMonorepoのルート階層を意味していて、全アプリケーションに適用する処理はこのときに実行するようにしています。
RUBY_VERSIONAPPLY_SEEDは各アプリケーションごとのCI実行に必要な情報を定義しています。
この場合だと、ルート階層での実行+アプリケーション3つそれぞれで実行ということになります。

matrix:
  include:
    - SUB_REPOSITORY: ''
      RUBY_VERSION: 2.4.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application1
      RUBY_VERSION: 2.6.0
      APPLY_SEED: true
    - SUB_REPOSITORY: apps/rails-application2
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application3
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false

2. あとでYAMLのエイリアスとして使えるようにアンカーを定義する

以下の部分はGitHub Actionsでいうところのon.*.pathsの設定にあたるもので、Monorepoのルート階層で実行するかアプリケーションごとの階層で実行するかを判別する設定です。これをyamlのアンカーとして定義してテストやlintの処理でエイリアスとして参照します。

references:
  if-root-matrix: &if-root-matrix
    matrix:
      SUB_REPOSITORY: ''
  if-app-matrix: &if-app-matrix
    matrix:
      exclude: SUB_REPOSITORY: ''

3. CIで実行するジョブを定義する

以下は実際にCIで実行するテストやlintの設定です。
lintはMonorepo内の全アプリケーション一律で同じものを適用しているのでルート階層で実行しています。
apply-seed-each-appというのは、特定のアプリケーションのみで実行したい処理の設定で、APPLY_SEEDをtrueにしているアプリケーションでのみ実行されます。

pipeline:
  run-tests:
    commands:
      - bundle exec rspec
    when:
      <<: *if-rails-app-matrix
  
  lint:
    commands:
      - yarn run lint
    when:
      << *if-root-matrix

  apply-seed-each-app:
    commands:
      - bundle exec rails db:seed
    when:
      matrix:
        APPLY_SEED: true

Matrix Buildsは各アプリケーションごとに何を実行するのかが一見しただけではわからないですし、アプリケーションごとに独自の処理が増えるとより複雑になってしまうという点がつらかったです。

GitHub Actionsの場合

今度はGitHub Actionsのサンプルコードです。
Monorepoのルート階層で実行する処理はroot.ymlに、各アプリケーションごとに実行する処理は個別にymlファイルを用意します。今回はrails-application1の設定ファイルを例としてrails-application1.ymlに記載します。

GitHub Actionsのコードについては詳しい説明は割愛しますが、ひと目見ただけでも各アプリケーションで実行する内容がなんとなく読み取れるのではないでしょうか。

name: CI

on: push

jobs:
  lint:
    steps:
      - name: Run Lint
        run: yarn run lint
name: rails-application1

on:
  push:
    paths:
      - 'apps/rails-application1'
      
jobs:
  run-tests:
    steps:
      - name: RSpec
        run: bundle exec rspec
  apply-seed:
    steps:
      - name: Apply seed
        run: bundle exec rails db:seed

コミュニティが大きくドキュメントや知見が豊富

他にもGitHub Actionsの方が公式のドキュメントが豊富で、知見がインターネット上に多いという点が魅力でした。
また自作のワークフローが多く公開されているという点も良いです。

GitHubとの連携機能が充実している

GitHubが提供しているだけあり、GitHubとの連携機能が豊富です。

  • GitHub上のPull RequestからCIの実行結果やログをスムーズに確認できる。
  • ワークフローのトリガーが豊富。
  • GitHubのトークンを発行する手間なく参照できる。

移行方法

GHESではCIが実行されるインスタンスを自前でホスティングする必要があることからはじめにインフラ構築をしました。
インフラ周りのお話についてはまた別途記事を公開する予定のためここでは割愛させていただきます。

その後、Droneで実行しているRuboCopやRSpecなどをGitHub Actionsに移行しました。
DroneからGitHub Actionsへの書き換えは技術的に難しいところはありませんが、GitHub Actionsの構文に慣れていなかったので勉強しながら書き換えていきました。

2020年10月ごろにActionsを導入し、Droneからの移行が完全に完了したのは2022年の11月ごろでした。2

所感

GitHub Actionsに移行してしばらく経ちましたが、やはりDroneよりも資料が豊富で開発しやすいと感じています。
また、GitHub Actionsでは複数のリポジトリでワークフローを再利用3しやすい点も便利だと思いました。
Droneでは共通化したい処理をDockerイメージにすることで共通化していたため、より手軽になったと思います。

一方でGitHub Actionsそのものというわけではないですが、GHESによる課題も少しありました。
というのもGHESはGitHub Enterprise Cloudと比べて機能追加が少し遅いです。当時はbundle installyarn installの結果をキャッシュするactions/cacheがGHESでは使えなかったため自作する必要がありました。(現在はGHESでも使えるようになっています)

まとめ

DroneからGitHub Actionsへの移行について紹介しました。
GitHub Actionsへの移行によって、よりCIの開発がしやすい環境になったと言えます。

シンクロ・フードではCIチームというCIに関する開発をするチームを設置していて、各開発者が所属するメインのチームと兼任する形で所属しています。
CIチームのメンバーも徐々に増えてきているため、今後はよりCI環境の改善を加速させていきたいと思います。


最後に、エンジニア募集のお知らせです! シンクロ・フードでは、まだまだ技術的な課題やチャレンジが残っています。 ぜひ、一緒に改善していきましょう!

また、飲食店ドットコム、求人飲食店ドットコムを始めとしたサービスの開発・改善も、どんどん進めております。 ご興味のある方は、以下の採用サイトからご連絡ください!

www.synchro-food.co.jp


  1. Monorepoへの移行についてエンジニアブログのこちらの記事にまとめています。こちらも良ければぜひご覧ください。
  2. 2年ほどかかっていますがCIの開発に割り当てられる時間があまり多くないというところによるものです。また、その期間は移行だけでなく並行してCIの速度改善などの取り組みをしていました。
  3. Composite Action

求人飲食店ドットコムの求人検索に OpenSearch を導入した話

こんにちは、シンクロ・フードの小室です。

シンクロ・フードでは弊社のサービスである求人飲食店ドットコムの求人検索にOpenSearchを導入しました。
どのように導入したか、また、導入した結果を紹介します。

opensearch.org

はじめに

今回、求人検索にOpenSearchを導入した目的は検索の高速化です。
また、今後求人の掲載数が増加したときにも検索性能を劣化させないことも求められていました。
そのため、MySQLよりもOpenSearchの方が大量のデータ検索に向いていることも採用した理由にあります。

同様の検索エンジンとしては、OpenSearch以外にもApache SolrやElasticsearch等が選択肢としてありました。
しかし、今回は主に運用の負荷を減らすため、AWSのマネージドサービスも提供されているOpenSearchを採用しました。

以前の求人検索

求人の検索に使用される情報はMySQLに保存されており、それに対して検索が行われていました。
そのため、OpenSearchで検索を行える状態にするためにはMySQLからOpenSearchへデータを同期する必要がありました。
また、これらのデータは複数のアプリケーションから更新されるため、このことを念頭に、OpenSearchへのデータの同期方法を考えました。

MySQLからOpenSearchへのデータの同期方法

データの同期を行うシステムの構成図は以下のようになっています。

データ同期システム構成図

各アプリケーションではデータが更新される箇所で更新が行われたレコードの主キーをSNSへ送信します。
rakeタスクではSQSをポーリングし、受け取った主キーを元にMySQLからインデックスの作成に必要な情報を取得しOpenSearchに対して更新を行います。

当初は定期的に更新を実行することを考えていましたが、よりリアルタイムに更新を行うためにこの方法を採用しました。
また、この方法ではデータの更新が行われるアプリケーションではメッセージをSNSへ送信するだけであるため、定期的に更新する場合と同様に更新処理の実装は一箇所で済み、実際の更新処理について各アプリケーションは意識しなくていいというメリットがあります。

OpenSearchによる検索

求人の検索は各種条件を元にOpenSearchに対して行います。
OpenSearch からは一致したものの主キーを取得し、MySQLから実際の表示に必要なデータを取得しています。

また、既存のフリーワード検索ではLIKEによる部分一致を行っており、この検索をOpenSearchで再現する必要がありました。
これはN-gram Tokenizerを使用し、検索クエリに match_phrase句(通常使用される match 句と異なり、文字列の出現する順番が考慮される)を使用することで実現できました。

また、icu_normalizerという文字の正規化を行うフィルターを使用することで、既存ではできなかった丸数字(①、…)と数字(1、…)、半角カナ(ア、…)と全角カナ(ア、…)等で区別せず検索できるようになりました。

テスト中に発生した問題

インデックスの更新に関して、ローカルでの開発中には起きず、テスト環境だけで起きた問題が主に2つありました。

1つ目はアプリケーションで行った変更が反映されていない古いデータでOpenSearchのインデックスが更新される問題でした。
具体的には、rakeタスクでデータを再取得するときに古いデータが取得され、そのデータで更新がされている状態でした。
この原因としてはトランザクションの中でSNSへのメッセージ送信を行っていることでした。
これはアプリケーションで自動的に張られるトランザクションについて把握できていなかったためにそのような実装にしてしまっていました。
この問題が確認できていないときは、たまたま、rakeタスクで更新処理が起きるまでの間にコミットされていて更新が成功している状態でした。トランザクションを明示的に張り、コミット後にSNSへメッセージを送信するように修正しました。

2つ目は、更新の負荷によって429エラーが起きていた問題です。
429エラーは、OpenSearchに対するリクエストが増え、処理が追いつかない場合に起きるようです。
この問題はローカルでOpenSearchのDockerイメージを利用して開発をしているときには確認できず、テスト環境でAmazon OpenSearch Serviceを利用して初めて確認できました。
今回は偶発的に(多数の更新が重なり)エラーが起きた場合には更新処理のリトライで対処をしました。
rake タスクでエラーが起きた場合に SQS へメッセージを戻して、再度処理を行っています。
その他ではバッチ処理で大量の更新をする場合もエラーが起きるため、こちらは個別に対処しました。
ここでのバッチ処理ではリアルタイム性は比較的求められていなかったため、更新を小分けに行いスリープを挟むことで負荷を軽減させています。

aws.amazon.com

導入した結果

1月12日に無事にリリースが完了しました。
以下はフリーワード検索と業態(イタリアン)の検索のレスポンスタイムの推移です。

レスポンスタイム

リリースの前後で、フリーワード検索で、平均510ms減り、業態(イタリアン)の検索では平均171ms増えました。
フリーワード検索では、テスト環境でも同様にフリーワード検索が速くなり、それ以外では既存と同等もしくは遅くなっているという結果になっており想定内ではありました。

この原因は単純な一致の検索だとMySQLで十分に速いというのとOpenSearchではアプリケーションとはREST APIでやり取りをするためMySQLよりも通信に時間がかかるためだと考えています。
これを改善するため、今後OpenSearchに対する複数のリクエストをまとめる等の対策を行っていきます。

まとめ

求人検索の高速化を目的にOpenSearchを導入しました。
更新方法をリアルタイムに近づけるためにSNS/SQSを使用したり、既存のLIKE検索を再現するために工夫したりしました。また、テスト環境で起きた問題に対処しました。

今回の目的である検索の高速化については、フリーワード検索ではレスポンスが速くなり、大きかったMySQLへの負荷も軽減できました。しかし、他の検索条件ではまだ効果が出ていません。
これは今後リクエストをまとめる等の対策で改善していきたいと考えています。

OpenSearchについては今回初めて導入したということで、分かっていないことも多いです。
今後、知見を深めより活用していきたいと思います。


最後に、エンジニア募集のお知らせです!
シンクロ・フードでは、まだまだ技術的な課題やチャレンジが残っています。
ぜひ、一緒に改善していきましょう!

また、飲食店ドットコム、求人飲食店ドットコムを始めとしたサービスの開発・改善も、どんどん進めております。
ご興味のある方は、以下の採用サイトからご連絡ください! www.synchro-food.co.jp

Redash と Google スプレッドシートを連携してデータ活用をちょっとだけ改善した話

こんにちは、シンクロ・フードの越森です。

1年ほど前からMリーグ視聴きっかけで、麻雀に再度はまり毎朝早起きして雀魂で麻雀を打ってから仕事を始めるという健康的な生活を送っています。

今回はシンクロ・フードでのデータ活用周りでちょっとだけ改善したことの紹介をしたいと思います。

はじめに

シンクロ・フードでは 2016 年に Redash を導入することで以下のことを実現しました。

  • サービス指標の可視化
  • サービスの重要指標と連動したSlack通知
  • データ抽出作業の自動化

redash.io

導入前はエンジニアがディレクターから依頼されて SQL を作成することが多かったのですが、導入してからはディレクターが SQL を作成してデータを抽出するようになり、スムーズにデータ分析ができるようになりました。

(複雑な SQL でデータ抽出する場合はエンジニアに作成依頼が来ることもあります)

Redash ではサービスのデータを記録している Aurora (個人情報を潰した状態)とログ情報やネイティブアプリのイベントを記録している BigQuery からデータを抽出できるようになっており、サービスの分析に活用しています。

わきあがる要望

Redash を導入したことでデータ分析活動が活発に行われるようになりましたが、それに伴いこういうことができないかという要望もあがるようになってきました。 (非常に良いことですよね)

上がった要望としては以下のようなものがありました。

  1. daily、weekly、monthly のその時点での数値を見たい
  2. 複数の指標を一つの表やグラフでまとめて見たい
  3. 数値を使って分析する際の CSV ダウンロードを自動化したい

1. daily、weekly、monthly のその時点での数値を見たい

シンクロ・フードが運営している飲食店専門の求人サイトである求人@飲食店.COM を例に出して説明します。

求職者の応募数については応募日時を持っているため、そのようなデータを抽出することは簡単ですが、掲載店舗数になると掲載期間としてデータを持っているため、抽出する SQL が複雑化して時間もかかるようになってしまいます。

(SQL によっては実行時間が長すぎて結果が返ってこないことも...)

求人@飲食店.COM では掲載期間中は複数の職種を募集することができ、職種の追加、取り下げもできるようになっていますが、その時の操作自体を記録していないと、後から見た場合にその時点で掲載されていたかどうかの判断がつかなくなり、その時点の掲載していた職種数を抽出できない問題があります。 データによっては記録していて抽出できるものと記録していなくて抽出できないものがある状態でした。

2. 複数の指標を一つの表やグラフでまとめて見たい

Redash ではダッシュボード機能があり関連している指標をまとめて見ることができますが、あくまで 1 つの指標単位での表やグラフのため、関連している指標をまとめて見て分析したいという要望があがりました。

3. 数値を使って分析する際の CSV ダウンロードを自動化したい

Redash では SQL の実行結果を JSON や CSV 形式でダウンロードする機能があり、ダウンロードした数値を利用してシミュレーションなどをしていました。 ただ、確認したいときに都度ダウンロードして数値を反映させることが手間なため、自動化できないかという要望があがりました。

Google スプレッドシートとの連携による改善

上記のような要望を満たす方法がないかと検討した結果、Redash の SQL の実行結果を定期的にスプレッドシートに転記するようにすることで今回の 3 つの要望については解決できると考えました。

2 と 3 の要望については、Google スプレッドシートに転記するだけで、あとは Google スプレッドシート側で参照設定を設定するだけで複数の指標を 1 つの表やグラフとして表示できるようになります。

(1 つのスプレッドシートに、複数の自動転記用のシートを作成して各指標を転記して、表示用のシート側で参照設定する形で実現しています。)

1 の要望については、定期的に転記するだけだと毎日上書きされていってしまうため、Google Apps Script(GAS)を利用して Redash からの転記後に更に別のシートに転記することで、その時点の状態が分かる情報を持っていないデータに関しても、時点情報を記録することができるようになります。

システム化

方針は決まりましたがセキュリティの問題があり、Redash と Google スプレッドシートだけでは連携することはできません。 そのため、連携するためのシステムを作成することにしました。 社内で利用する簡易的なシステムということで CTO の大久保がその時に興味を持っていた Racket で実装しました。

racket-lang.org

システム構成は、以下の図になります。

Redash 連携システム構成図

連携プログラムが実行された後の処理の流れは以下になります。

  1. 設定用のスプレッドシートから Redash の API の URL 、転記先のスプレッドシートのURL、転記先のシート名の3項目を取得する
  2. Redash の API を実行して JSON 形式で SQL の実行結果を取得する
  3. 転記先のスプレッドシートの対象のシートに転記する

まとめ

今回の要望を対応したことで、分析する人の手間が減り、今まで確認することが難しかったデータも簡単に確認できるようになり、データをさらに活用できる状態になりました。

非常に良いことずくめに見えるのですが、1点だけ問題がありました。

最初に想定していた以上に活用された結果、設定用のスプレッドシートの記入で不備があることでエラーが発生して、調査の時間を取られることが増えてしまいました。 また、プログラムが Racket で実装されていることからプログラムを読める人も少なく CTO 自身が対応することも度々...

この問題についても先日、エンジニアの新卒研修の一環でこのシステムを Rails でリプレースすることで解消しています。

(設定でもバリデーションをかけることができるようになりました。)


最後に、エンジニア募集のお知らせです!
シンクロ・フードでは、今回のちょっとした改善に限らず、まだまだ技術的な課題やチャレンジが残っています。
ぜひ、一緒に改善していきましょう!

また、飲食店ドットコムを始めとしたサービスの開発・改善も、どんどん進めております。
ご興味のある方は、以下の採用サイトからご連絡ください! www.synchro-food.co.jp

Aurora MySQL v2 (MySQL 5.7 互換) へのアップグレードとスムーズなテスト

こんにちは、シンクロ・フードの日下です。
最近は、健康のためラジオ体操を毎日の習慣にできるよう取り組んでいます。

シンクロ・フードでは、2022/10/05 の 05:00 から 08:00 の間、サービス停止を伴うサイトメンテナンスを実施し、Aurora MySQL の v1 (MySQL 5.6 互換) から v2 (MySQL 5.7 互換) へのアップグレードを行いました。 本記事では、アップグレードのために行った変更、またスムーズなテストのために工夫したことを記します。

なぜアップグレードしたのか

なぜなら、Aurora MySQL v1 のサポート終了が発表されたからです。

aws.amazon.com

飲食店ドットコムを始め主要なサービスで Aurora MySQL v1 をメインの DB として使用しているため、対応が必要でした。

アプリケーション側の変更

シンクロ・フードでは、サーバーサイドでは以下のフレームワーク・言語を使用しており、それぞれ DB 接続用のライブラリの更新が必要か確認しました。

  • Ruby on Rails (Ruby)
  • Seasar2 (Java)

Ruby on Rails で使用している Mysql2 については、既に MySQL 5.7 と互換性のあるバージョンを使用しているため対応が不要でした。

Seasar2 で使用している MySQL Connector/J は、MySQL 5.7 未対応の 5.1.23 を使用していたためバージョンアップしています。
バージョンアップするバージョンは、MySQL 5.6、5.7 の両方をサポートする 8.0.27 を選択しました。
なぜなら、MySQL 5.6 がサポートされているため、Aurora MySQL v2 へのアップグレードより先にバージョンアップできるからです。
先にバージョンアップを行うことで、Aurora MySQL v2 へのアップグレード当日の手順を減らせます。
また、Aurora MySQL v2 へのアップグレードで問題が発生した場合も、素早く v1 へロールバックできます。

インフラ側の変更

インフラ側の変更で 1 番悩んだ点は、アップグレード方法の検討でした。
検討していた時 ANDPAD 様の以下の記事を読み、アップグレード当日の作業を最小にできる事からシンクロ・フードでも binlog レプリケーションを使った Blue-Green Deployment を採用しました。

tech.andpad.co.jp

CNAME を使ったエンドポイントの指定や、ROW_FORMAT の変更に伴う長い DDL の実行など、とても参考にさせていただきました。
ANDPAD 様、素敵な記事をありがとうございます!

また、具体的な手順については、以下の AWS の記事を参考にしています。

aws.amazon.com

アップグレード後は、Aurora MySQL v2 から v1 への逆レプリケーションを設定して、万が一の際は素早くロールバックできるようにしています。
新しいバージョンから古いバージョンへのレプリケーションは公式にサポートされていませんが、事前にテスト用の環境で問題が発生しないことを確認しました。

テスト

シンクロ・フードでは、社員しかアクセスできないテスト用の環境を用意しており、開発中の機能のテストを行っています。
Aurora MySQL v2 についても、テスト用の環境でアップグレードを行いテストしました。

テストではエラーにならないことの他に、パフォーマンスが劣化しないことを重視しました。
しかし、パフォーマンスのテストと言っても、ただページへアクセスするだけでは Aurora MySQL v1 と比較して劣化しているかどうかの判断は難しいです。
また、影響する範囲が広く、SQL を 1 つ 1 つ確認するのは時間がかかりすぎると考えました。

そこで、HTTP リクエストを実際に送信し、ログから実行された SELECT 文を取得して、Aurora MySQL v1 と v2 で比較するテスト用の機能を作成しました。
以下の図が、機能の概要です。

テスト用の機能

  1. まず、テストしたいアプリケーションに対して HTTP リクエストを送信します。
  2. HTTP リクエストの送信時刻とレスポンスが返ってきた時刻で処理時間を計算し、処理時間中のログ (general log) を Aurora MySQL v2 から取得します。
  3. 取得したログから SELECT 文を抜き出し、Aurora MySQL v1 と v2 に対して実行して結果を取得します。
    SELECT 文の EXPLAIN も実行して、実行計画も同時に取得します。

実際に、テスト用の機能で表示される SELECT 文の比較結果の一例が以下になります (お見せできない部分は黒塗りしています)。

SELECT 文の比較結果

Aurora MySQL v1 と v2 の実行計画を表示しており、差がある箇所は赤色の太文字で強調しています。
また、Aurora MySQL v1 と v2 での SELECT 文の実行時間の差を表示、参考できるようにしました。

EXPLAIN の実行計画を比較することで、DB の負荷などを考慮せずにパフォーマンスが劣化しているか確認しやすくなりました。
さらに、テスト用の環境でも Aurora MySQL v2 から v1 へ逆レプリケーションを実施していたため、同じデータ量で比較ができています。

このテスト用の機能と EXPLAIN の見方を各サービスの開発者へ案内することで、スムーズにテストを行えました。
テスト方法はどんなアップグレードでも悩む点ですが、今回はユニークな方法で上手くテストできたと考えています。

リリース

テストが完了し、2022/10/05 の 05:00 から 08:00 のサイトメンテナンスでアップグレードを行いました。
アップグレードについては、Blue-Green Deployment によって Aurora MySQL v1 から v2 へ接続先を切り替えるだけということもあり、無事メンテナンス時間内で終えることができました。

アップグレードしてから、もうすぐで 1 ヶ月経ちますが現在まで大きな障害は発生していません。

まとめ

無事に Aurora MySQL v1 から v2 へのアップグレードを行えました。
本記事が、アップグレードへ取り組んでいる方や取り組もうとしている方の、お役に立てれば幸いです。


最後に、エンジニア募集のお知らせです!
シンクロ・フードでは、今回の Aurora MySQL アップグレードに限らず、まだまだ技術的な課題やチャレンジが残っています。
ぜひ、一緒に改善していきましょう!

また、飲食店ドットコムを始めとしたサービスの開発・改善も、どんどん進めております。
ご興味のある方は、以下の採用サイトからご連絡ください! www.synchro-food.co.jp