Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキが運営しています。

インターンで好き勝手働かせていただいた話

エンジニアインターンとして二週間アカツキで就業したstalagmiteさんの体験記をお届けします。「自分の理想とするエンジニア」を意識して働いたインターン、どのような経験をされたのでしょうか。その様子をレポートいただきます。

自己紹介

この度アカツキのもとで内定後インターンをさせていただきました。その時のことについて、主に自分が何を考えながら動いたかを書きたいと思います。 とても自分語りが多くなるかもしれませんが、多分アカツキの社風的に大丈夫だと踏みました。

所属当初の背景

大学では情報系の専攻をしていた。
大学での研究はOpenStreetMapのことについてやってる。
ゲーム製作サークルに所属していて、学祭でゲームの展示をしたりしていた。

プログラミング経験

C++,Haskell,C#,Unity,GLSL

隙間時間で調べてること

数学(圏論、集合論、確率論)
シェーダ
VR

普段やっていること

DxLibやOpenSiv3Dを触りつつゲームの部品を作ったりモックを考えたりしている
しかし完成したものはないという地獄

所属時の話と自分語り

大学でプログラムの勉強をしていると、プログラマーというのは「厳密に仕様を決めて高い水準で要求に答える」みたいな、専門家としてのSE像があった。 ただ、もともと大雑把な性格で緻密なことが苦手な自分は、「そういうイメージのプログラマになれるのか」という漠然とした不安があった。(もちろん、ある程度のコード力はあると自負はしているけれども、その専門家としての価値と自分の性格って合わないような気がしていた)
就職活動をしている中の、そんな漠然とした不安のを抱えているときに、アカツキとの面談や、会社の説明を聞く中で、

「うちで働いているエンジニアはそれぞれ違う部分で価値を出し働いている。」
「インフラのことについての専門性で貢献しているエンジニアもいれば、プランナーなどの別職と連携をとったり、自分で案を出しつつ実装までするエンジニアもいる」
「全員が自分で考えていて、受け身なだけの人はいない」

という社風に興味をひかれ、アカツキの入社を希望した。

インターンの目標

インターン初日、メンターの方に言われたのは、
「このインターンでの目標を決めましょう」 という抽象的なものだった。 働き方も、目指すものも、全部自分で決めて良い。
具体的な課題をひたすら潰すもよし、実際のプロダクトの小さい機能を設計から作ってもよし、「 インターンで僕が何か良いものが得られたと感じられる、それがこのインターンの意義です」と説明してくださった。
僕は、大手のゲーム業界でたまに、規模は大きいのにゲーム性そのものがイマイチなゲームというのがあるのが嫌だった。
これは入社前から持っていた偏見であるが、「イマイチになってしまうのは、プランナーが仕様を考えてエンジニアが実装して、それをプランナーに戻してまたフィードバックして、というサイクルが重いのが原因なんじゃないか」と思っていた。だから、僕はこのインターンの目標として
「プランナーの考えている抽象度の高い案をとっととモックを作って実現して改善する、というプロセスをぶん回すエンジニアになりたいです」
と答えた。
一つ重大な問題がある。僕は自分の「ゲームエンジニア」としての像をここに置いた。この作業はいわゆる「ヒアリングから問題点を聞き出し解決策に落とし込む」というまさにSEとしての分野であり、
僕は大学でこの内容の授業の単位を落としているのである。
ついでに言うともともと僕はコミュ障であって、就活中にも自己嫌悪で大変なことになっていたほどだ。もう不安しかない

最初の1週間

目標を決めた後、ソーシャルゲームの開発(クライアント)チームに配属された。このチームでは、プランナーエンジニア問わず、Trelloに

「何か改善すべき問題」
「さらにゲームを面白くするためのコンテンツ案」
「UIUXの改善案」

など、色々な抽象段階の案を共有することができることを教えてもらった。
ここから自分が面白そうな案、実現できそうな案を拾い、話を聞きに行く。
プランナーがしたいことを、現在実装がどうなっているかを把握できるエンジニアが聞き、仮設計を考え形にする
これは結構自分がやりたい立ち回りに近いものだと思えた。

ここで色々な意見を聞いていく中で、「ゲーム内のキャラクターの編成を組むときの、画面遷移を伴う操作が煩雑であり、それを改善したい」という意見をプランナーSさんに頂き、これは手を付ける影響も少なくて済みそうだし、いい感じに抽象的だったので、担当することにした。 幸い、個人的にプログラムのバイトの経験があったので、さほど時間を取られずに自力で規模の大きいコードを読みつつ、既存コード内のでのゲームデータの扱い方や簡単な画面遷移の処理を頭に入れ、最初の1週間のほとんどをヒアリングに使った。
ここで最初の山を迎えた。最初の実装、設計案を考えたときに、「これは難しい。既存の画面遷移の中にどうUIUXをいじっても、複雑な操作が発生してしまい、意味がなくなってしまう」という結論になってしまった。このことをプランナーSさんに相談しにいくと、
「もっと根本から考えてみたほうが良い。本当は、編成なんて1タップでできるんじゃないか?それが実現できたら、最も便利なんじゃないか?
僕は仕事として、与えられた役割としてプログラマを引き受ける時に、どうしても今確実にできることベースで物事を考えてしまう癖がある。それは情報系プログラマ、エンジニアとして大事な要素ではあるが、何かを解決したいときの思考方法ではない。なまじコードが読めるから、設計案が頭に浮かぶから、わかる範囲で片付けようとしてしまっていた。
なので僕は「1タップで自動編成」を一番上に置いて、それにもっとも近づくように、要求を分析することにした。これは、他のエンジニアに相談すると、「完全に自動編成するためには、キャラごとに編成する際の評価値を計算する必要があるが、その厳密な計算は難しい」
ということになってしまったのだが、
「他ユーザの編成を分析してそれを元に評価値を作れば、厳密な計算はいらないし、信頼性も高いのではないか」
という案が浮かび、既存の資産も活かせるということで、落とし所として、
「同レベル帯で成績の良い他ユーザの編成(=クリアログ)を自分の手持ちキャラなるべく似せて自動編成できる機能を作る」
とういことになった。(プランナーとエンジニアを行ったり来たりして解決案をだすというムーブ、これがしたかった)

これが決まった後、既存のコードを利用した設計と実装案、ドキュメントを書くことができた。
ここまで来るのに二度提案を練り直した。 この時点のドキュメントで
関数の影響範囲
この提案で操作が便利になるユーザー層の想定
までは書くことができた。ここまでは、いいペースだったと思う。

次の1週間

手をつけた課題についてドキュメントを書き、仮のコードも書いて、最初のプルリクエストを飛ばした。 当初の予想では、もっと議論が活発になると予想していた。というのも、僕が担当したのはロジックで、仮提案であり、現状のコードや設計により詳しい人にコメントがもらえるだろうと予測していた。ロジックだけでなく、本実装する際はUIUXの話も絡んでくるだろうと思っていたので、正直なところ「あれ、ウケが悪いな」と思った。
この時、元々の提案を出してくれたプランナーSさんが別の部署に行ってしまい、次にやることも含めて自分で考える必要が出てきて、3日ほど右往左往してしまった。

その後なんとか僕は、「現時点での提案の「自動編成の精度」が、どの程度良いのか分析できていないから、皆現在の提案の進捗が想像が難しいんだ」、と考えた。それは部分的には真で、自分でも「じゃあこの自動編成を使うと、どう便利になるの?具体的にどのユーザがどのように便利になるの?」という風に疑問に思っていた。なので次は、
「既存のユーザの分析」
「この提案で便利になるユーザとはどのような状況の人間か」
を調べることにした。

最後の1週間

mysqlをいじってアプリで使われているデータから、ユーザの手持ちキャラ、自動編成の通してみた結果を比較したデータを用意し、分析した。具体的には、自動編成後の編成のスコアの分布ごとに、
「編成結果のスコアがここのユーザたちは、ゲーム内ではこのコンテンツをするためにこの操作をすると考えられる」
編成結果の低いここのユーザ層は、そもそもここのコンテンツを触らないな。より良いコンテンツへの推奨はUIUXでこう解決できるな」
というところを分析し、ドキュメント化した。
また、このデータを他のエンジニアが検証できるよう、デバッグメニューの実装し、プルリクエストを飛ばした。 これによって、2回目のコメントもらい、時間的にも区切り的にもちょうど良いので本インターンでの作業を終了した。

総評

一応、ヒアリング、提案、実装、分析、デバッグメニューで他者に検証してもらうフロー、今後の展望のドキュメント、までを、いろんな人に話を聴きながら作り上げることができたので、最初の目標である、
「プランナーの考えている抽象度の高い案をとっととモックを作って実現して改善する、というプロセスをぶん回すエンジニアになりたいです」
という動きはできたのではないかと思う。
ただ反省点として、
「プランナーSさんがいなくなったタイミングで、この提案の実現の進路を考える人が自分以外にいなくなったことに気づかず、失速していた」 というのがあげられる。
本来これにすぐに気づき、次に決めるべき内容、この提案について本腰を入れて一緒に考えてくれる(自分以外の)人間を巻き込む必要があった。 (ずっとメンターにも相談していたのだが,いつも「次どうすればいいですかね」と聞いてしまった。僕が考えるべきだったことなのに)
確かに、僕はプルリクを飛ばしてエンジニアとしてコードを書いてドキュメントを書いて分析もできたが、じゃあこれが何かの役にたったのかというと、正直よくわからない。なぜなら
提案の進路、実現の持っていき方を考えていなかった

or
それを考えてくれる人間の確保が必要なことに気づかなかった
からだ。
「プランナーの考えている抽象度の高い案をとっととモックを作って実現して改善する、というプロセスをぶん回すエンジニア」
のそれっぽい動きはできたけれども、そういう立ち回りののオリジナルな価値を生み出せたかというと、それは少なかったように思う。
そもそも僕がやりたいこの動きって、「プランナー<->エンジニア間で速度の速いフィードバックをして、抽象から具体に落とし込む」というのをやりたかったのだ。 しかしそもそも、
「抽象的な目標、提案をチームの内側から通すって時は、具体的な設計、方針が決まるまでその提案の立ち位置、自分の立ち位置、他人の立ち位置をよく把握しなければならない」
、そうでなければ、空中分解しかねないという基本を、僕は知っておくべきだった。
これはものを考えるプランナーだけでなく、何かを作るという当事者全員が意識すべきことだったことに気づいた。
三週間のインターンなので、大きい規模を実現することはできないという前提があったなかで、最大限のことをしようと覚悟をしたはずなのに、その覚悟がまだ足りなかったように思う。
インターンが終わったらまた日常生活に戻る。そこには、大学でも、個人ゲーム制作チームでも、やり残していることがある。
来年アカツキに入社するまでに、それらにケリをつけて成長して出直そうと思った。

アカツキインターンで体験したこと・感じたこと

エンジニアインターンとして二週間アカツキで働いたnkzさんの体験記をお届けします。初めてのインターン、そして初めての実践的な開発に関わったということでしたが、どのような経験をされたのでしょうか。その様子をレポートいただきます。

ご挨拶

初めまして。nkzです。この度アカツキで、あるモバイルゲームのサーバサイドエンジニアとしてインターンをさせていただきました。趣味は競技プログラミングでそれ以外の開発経験はほとんどありませんでした。開発経験の無い競プロ勢がインターンに行ったらどうなるか?という一つのモデルケースにしてもらえれば幸いです。期間としては二週間ちょっと、開発環境はRuby on Railsでした。

インターンが始まるまで

アカツキのインターンに行くことを決めたのがインターン始まる二週間前とかで準備する期間はあまりありませんでした。一応メンターに「Rails Tutorialを最低7章までやっておいて」と言われたので、7章までやりました。
Webアプリケーションを作った経験はほぼ無くて、N予備校のプログラミングコースでNode.jsのExpressというフレームワークを使ってなんかやったくらいなんですが、なんかそれに比べるとかなり使いやすい印象を受けました。何となくRailsが流行っている理由が分かりました。

初日

インターンに伴うオリエンテーションや社内ツールなどの設定をやっていました。この日はメンターが不在だったので、フリースペースで人事の方とお話ししながらまったり過ごして早めに帰りました。

環境構築 〜 最初の課題(N+1問題)

二日目からメンターと顔合わせをして、まずは環境構築から始めました。ドキュメントを読みつつメンターに教えてもらいつつやりました。私が勝手にmysqlの新しいバージョンをインストールしたせいで、他の箇所でエラーが出たりして、環境構築は辛いと思いながら何とかできました。

そして最初の課題は「あるAPIでN+1問題が起こってそうだからそれを解決してみよう」というものでした。

「N+1問題」は有名な問題らしいのですが、私は「そもそもN+1問題とは……?」という状態でした(名前は何となく聞いたことがある)

幸いにも有名な問題なのでググるとたくさん解説ブログなどが出てきてくれて、以下のようなブログが参考になりました。
ruby-rails.hatenadiary.com

感覚としてはデータベースのサイズをN, クエリの数をQとするとN+1問題が起こっている状態ではO(NQ)くらいかかっている状態で、それをO(N)になるように修正したい、ということなんだろうと解釈しました(合ってるかな…?)

ともかくグーグルによってN+1問題とは何かは分かりましたが、次にそれがどこで起こっていてどう修正すればいいのか、ということを突き止めていかなければいけませんでした。

プロジェクトのソースコードは大規模でRails Tutorialで触ったようなサンプルアプリケーションとは比べ物にならないくらいファイルが多くて、まず「ソースコードを読む」ことすら困難でかなり苦労しました。

ようやく「ここかな?」と思うところを見つけてコードを書き直してみたものの、メンターに「そこじゃないよ」と言われてしまい……最終的にはお手上げ状態になってしまい、ソースコードやログの読み方を教えてもらいつつ「ここを直せばN+1問題が直るね」と教えてもらって「なるほどな〜」となってこの課題は終わりました。私は何も出来ませんでした…

とても勉強にはなりましたが、自力では何も出来なかったので落ち込みました。「まぁ何の知識も無い状態からスタートしてるしこういうもんだよね」という感じで切り替えて行くことにしました。ここまでで三日経ちました。

二つ目の課題(社内ツールのビューを作る)

二つ目の課題は「シナリオチームが使っている社内ツールがあって、変換部分は既に作ってあるから操作するためのビューを作ってください」というものでした。この辺りで私が想像していたサーバサイドエンジニアとは少し違った課題になりつつありますが、そういう仕事もやるんだという感じでやっていきました。

前回見たプロダクトのコードよりもかなり小規模で、Rails Tutorialをやったくらいの人間にはちょうどよかったです。また一から新しいものを作るというより、既存のコードを参考にしながら作れる部分がかなり多くてそれも良かったです。

またここで初めてRSpecというテストフレームワークを使ってテストを書きました。「ブラウザの操作をシミュレートして期待する動作をしているか?」みたいなこともヘッドレスブラウザを使って簡単に実装できるらしく、凄いと思いました。テストを書くのは大変だけど面白いと思いました。

この課題ではたまにメンターに助けてもらうこともありつつ(turbolinkのせいでCSSが当たらない、テストの文法が間違っていてブラウザの挙動がおかしいなど)、自分で調べたり考えたりしながら進められることも多く最終的にPRをレビューしてもらってOKを出してもらって、達成感がありましたし少し自信がついたので良かったです。自分のレベルに合った課題を割り振ってもらっているおかげですが。ここまでで五日経ちました。

三つ目の課題(社内ツールの機能修正)

次の課題は前回の社内ツールの機能に不具合があるのでそこを修正するというものでした。

この課題はそんなに難しい点は無かったので割とサクサク実装することが出来ました。先頭にBOMが付いている文字列と付いていない文字列を同値比較していてデバッグ出力してもぱっと見同じに見えるのになぜfalseが返ってくるんだ…?というところで若干ハマりそうになりましたが…

メンターには「ファイルを直接触るようなテストは環境に依存する可能性があるので、ファイルに書き出す文字列を返すような関数を作ってそれをテストする形にしたほうが良い」ということを教えてもらったりしました。

あとこの辺りからコミットメッセージを以下のgistを参考にしながら英語で書くように努めました。(あまり悩みすぎるところでは無いと思っていたので文法とか適当ですが…)
[転載] gitにおけるコミットログ/メッセージ例文集100 · GitHub

ここまでで七日経ちました。

四つ目の課題(社内ツールの入力フォームを作る)

四つ目の課題は手がけていた社内ツールの入力フォームを作って入力された値のバリデーションをしてごにょごにょやるというものでした。これがインターンの課題で一番大変でした。

とりあえず最初はモデルを作らずに入力フォームのビューを作ってバリデーションはそのままparamsに入っている値を直接見て有効な値で無かったらエラーメッセージを表示させて…みたいな方法でやろうとしていました。モデルを作らなくていいと思った理由は入力フォームの値をDBに保存したり読んだり、みたいな操作が必要無いのでモデルを作らないほうがいいのかな?と思ったからです。しかし実装している途中で「これいちいち空チェックのバリデーションを自分で実装するのは筋が悪い気がする…ActiveRecordを継承すればその辺楽になるのに…」と思って、DBにアクセスせずにモデルを作るってありなのかな?と思って調べてみると…ありました。

qiita.com

どうやら include ActiveModel::Model と書けばいい感じにバリデーションのメソッドを使えたりViewに紐付くように出来たりするようです。

というわけでモデルを作ってバリデーションはそれに任せる感じにしたらだいぶいい感じになりました。

次にやりたいのは「フォームの入力に失敗した時にフォームに入力した値を保持した状態でリダイレクトさせる」ことでした。これもなかなかいい実装が出来ず最初は失敗した時に入力フォームの値をセッションに一時的に保存してレンダーする時にその値を埋めてセッションに保持した値を消して…みたいなことをしていて「うーん」という感じでした。これもメンターに「いい書き方無いですかね…」と相談したところ、リダイレクト先にフォーム情報をクエリ文字列として渡すという方法でシンプルに解決することができました。

他にも色々メンターやWeb業界で働いていた人にもレビューをもらって様々な学びがありました(form objectの話とか)。また色々ハマったこともありました(form_withの挙動とか)。振り返ってみるとこの課題が一番苦労したなぁ…という感じです。その分達成感も一番あって自分でも頑張ったと思います。

ここまでで十一日経ちました。

五つ目の課題(社内ツールの機能追加)

インターンも終わりに近づいて残すところ後二日となりました。最後は手がけていたツールの機能追加…というか機能拡張ですかね。今まではViewを弄っていましたが今回はModelを作る感じでした。「後二日で終わるのかな…」という感じでしたが、ソースコードを読んでいると割と既存のコードを流用できる部分が多くてほとんどコピペして後は少し修正してテストを書くぐらいの作業で特にハマるポイントもあまり無く無事終わらせることができました。

開発以外にやったこと

以上がインターンでやった開発の内容です。インターンの仕事内容としては上記の開発がほとんどですが、それ以外にも色々なことを体験させてもらったのでずらっと書いていきます。

朝会(あさかい)

インターンは10:00出社なのですが、毎日10:05から朝会というものがありました。これはチーム全体で5分程度の時間を取って全体に共有したいことなどを伝える時間です。だいたいプランナーさんが売上状況とかゲームがどれくらい遊ばれているかとかイベントの状況などを共有することが多かったです。

昼会(ひるかい)

毎日14:30からは昼会というものがありました。朝会はチーム全体での共有でしたが、昼会は開発チームで集まって15分程度の時間を取って共有事項を伝えたり、「今まで自分が取り組んできたタスク・これから取り組んでいくタスク」を全員が一言二言で共有するという時間です。エンジニアは自分の開発にかかりきりになるとどうしても周りが見えづらくなってしまうことがあると思いますが、この時間があることでみんながどういうことをやっているのかを把握できるので良い取り組みだと思いました。

エンジニアミーティング

隔週火曜日17:00からはエンジニアミーティングがありました。これはチーム単位では無く社内全体でエンジニアが集まって外部から招いた人の話をしたり、インターン生の紹介をしたり、自分が作ったツールをみんなに紹介したり…という時間です。最初のエンジニアミーティングではセキュリティに強い人を招いたお話があってCTFの可視化の話があったり、バイナリかるたとかアセンブラ短歌という世界があるという話を聞いたりしてよく分からなかったですが「すげー」と思いました。

1on1

毎週火曜日は30分程度のメンターとの1on1がありました。業務中はなかなか落ち着いて話す時間も無いのでこういう時間を設けてゆっくり話をする、みたいな感じです。特に大層な話をしたわけでは無いですが、インターンのフィードバックを少しもらったりアカツキという会社について聞いたりその他は雑談をしていました。私は競プロをやっているのでその話をしていたら、この前マラソン系のコンテストで出た問題がメンターが10年くらい前の高専プロコンで取り組んだ問題と実はほぼ同じだった、という面白い話を聞けたのがよかったです。

インターンの成果報告

エンジニアのインターン生は私以外にも結構いました。インターン生は最終日に成果報告をすることになっているので、私も他のインターン生の発表を聞きに行っていたのですが、AWSの環境構築をガッツリやっている人やゲーム内のミニゲーム(正の得点を得ることすらめちゃくちゃ難しい)を作っている人の話を聞いて、みんな凄いレベル高い…と思って聞いていました。刺激を受けました。

ランチとか飲み会とか

たまにメンターが他の人を誘って一緒にランチに行ったり、インターン生が卒業するときに一回大々的に飲み会が行われたりしました。ランチでは他のプロジェクトチームの話を聞いたりすることができました。インターン卒業飲み会でアイマスにめっちゃ情熱を注いでいるインターン生がアイマスをめちゃくちゃ推してくれたのでちょっと見てみようと思いました。

インターンで感じたこと・学んだこと

ゲームはいろんな人が協力しあって出来ている

当たり前っちゃ当たり前ですが、実際みんなと同じフロアで働くことでより強くそれを感じることができました。エンジニアだけでは無く、プランナーさんが企画したりディレクションをしてくれたり、シナリオを作ってくれる人、絵を描いてくれる人、音を作ってくれる人、モーションを作ってくれる人、検証をしてくれる人……様々な人の協力で一つのゲームが作られていてそれって凄いことなんだ、と感じました。

アカツキの働く環境はめっちゃ良い

アカツキはオフィス環境作りにもかなりこだわりを持たれていて、そのおかげでみんながストレス無く働ける環境になっていると感じました。靴を脱いで素足で仕事ができるとか、各階におしゃれなカフェっぽい休憩スペースがあっていつでも自由に休めるとか、オフィスに緑があったりして心が安らぐとか(?)。オフィスにいるのが苦痛になってしまったら仕事も辛いものになってしまうと思いますが、オフィスがおしゃれで居心地が良いのでそういうことは全く無く非常に良かったです。ただトイレに行くときに靴を履かなきゃいけないのがちょっと面倒ですが…

またオフィスだけでは無く、一緒に働く人たちも皆それぞれ違った個性を持ちつつも良い人が多いんだろうという印象を受けました。今回のインターンでは他の人と一緒に仕事をするという感じでは無かったので、あくまで普段のオフィスの雰囲気を見て、という感じですが。話し合うことはたくさんあるだろうし、ときにはすれ違いなどもあると思いますが、ギスギスした感じでは無く笑いもありながら和やかな雰囲気で皆さん仕事に取り組んでいました。

終わりに

始めにインターンのお誘いをいただいたときは正直かなり悩んでいました。行った方が良いんだろうな…と思いつつ不安な部分も大きくて「行きます」と言った後も「やっぱり辞めとけば良かったかな…」と考えたりしていました。

それでも実際に来てみると、やっぱり大変なことやしんどいこともありましたが、それ以上に良い環境で働けて学ぶことや感じることも多くてトータルで見ると「来て良かった!」と思えるものでした。この度インターンでお世話になった人事の方やメンターの方、一緒に働いてくれた方々には本当に感謝しています。

それでは長くなりましたが、ここまで読んでくださってありがとうございました。

カスタマーサポートに便利なMariaDB Spider エンジンを、Docker上に移設して保守も便利にする

はじめに

ゲームの運営では日々お客様から「ログインが出来なくなってしまった」「受け取れるはずのアイテムが受け取れなかった」などのお問い合わせをいただきます。

そのお客様が仰ったことが本当に発生したかどうか、あるいは本当とすれば原因は何かを考えるために、私達はそのお客様のデータや操作ログをサーバーのデータベースに保存しております。

さらにはゲームのイベント開催情報やカードのステータスなど、運営に関わるデータ(マスターデータ)と横断的に照らし合わせた上で、判断しております。

ここではそんなカスタマーサポートに役立てているMariaDB Spiderエンジンの紹介と、今回それをDocker上に移設して保守も便利にした話を書こうと思います。

MariaDB Spider エンジンとは

Spiderエンジンはデータベースのエンジンでありながら、内部にデータを保存することは行いません。

代わりに、テーブル構造とデータのあるホストを指定すると、Spiderエンジンのテーブルでクエリを実行するたびに指定したホストからデータを取得してくれます。

Spiderエンジンについての詳細はこちら

さらにはカラムの値によってデータベースの接続先を振り分けることも可能なため、1つのSpiderエンジンのテーブルから、水平分割されたデータベースからクエリに合わせて自動的に適した場所にアクセスすることも可能になります。

なぜSpiderエンジンがカスタマーサポートで便利なのか

私達のチームではデータベースにかかるインフラの負荷上、マスターデータやユーザーデータおよびログを1台のサーバーに集約することが出来ません。

さらはユーザーデータやログについては、1台で全ての負荷を受け止めることは出来ないため、さらにユーザーごともしくはテーブルごとにそれぞれ複数台に分割してデータを管理しております。

一方で様々なデータを横断的に検索するカスタマーサポートでは、もちろん出来ればデータの内容に合わせて手動で参照先を切り替える面倒は省きたいですね。

そこで、一つのSpiderエンジンから複数に散らばった必要な全てのデータを取得できるよう構築することがカスタマーサポートに大きく役立ちます。

Docker上で使用する理由

一方でSpiderエンジンはややマイナーなエンジンであるため、AWSのRDSから作ったデータベースにはインストールされておりません。 そのためAWSを使用している場合は、RDSを使うのは諦めてEC2インスタンスの上にMySQLサーバーを立ち上げた上で、Spiderエンジンをインストールする必要があります。

従来はSpiderエンジンを直接EC2インスタンスにインストールしていましたが、以下の問題を抱えていました。

  • データ構造や設定が管理されていないので、サーバーの立て直しが困難になってしまった。
    • その結果、EC2インスタンスのOS等のバージョンアップがしづらいので古くなってしまった。
  • アプリケーションのバージョンアップに伴いデータ構造が変わる際は、Spiderエンジンのデータベースにも都度合わせて手動で差分クエリを発行して変更する必要がある。そのためミスや抜け漏れによって実データベースと構造の差異が発生しやすい。
    • 特に使用頻度の低い場所については適切なアップデートがされなかった結果、過去に発生した構造の差異を埋めるのも困難な状態になってしまった。

そこで、今回はOS等のバージョンアップのためサーバーを立て直すのと合わせて、今後保守を行いやすいようDocker化を行うことにしました。

実装

まずはDockerfileの中身から見ていただけたらと思います:

FROM mariadb:10.3-bionic

RUN apt-get update && apt-get -y install \
  ssh \
  mariadb-plugin-spider \
  gcc \
  make \
  libssl-dev \
  libmariadb-dev

RUN cp /usr/share/mysql/install_spider.sql /docker-entrypoint-initdb.d/00_install_spider.sql

COPY 01_init.sql /docker-entrypoint-initdb.d/
COPY create_tables.sql /root/scripts/

COPY spider.cnf /etc/mysql/conf.d/
COPY mysql_init.sh /usr/local/bin/

RUN sed -i -e "s/exec \"\$\@\"/exit 0;/" /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["mysql_init.sh"]

やっている内容としましては、

1. Spiderエンジン・MariaDB起動に必要なライブラリを追加します
2. /docker-entrypoint-initdb.d/ に、Docker起動後に実行したいSQL文のファイルを指定します
  • まず、Dockerイメージの中にSpiderをインストールするためのSQLファイル /usr/share/mysql/install_spider.sql があるので、それを移します。
  • またデータベース起動時に実行させたいSQLファイルも合わせて作成し、移します。(今回は例として 01_init.sql に起動時に必要なユーザーやデータベース等の設定を記述します。)
    • なお、テーブルの追加 (create_tables.sql) については後述する理由(「ハマったポイント」を参照)から別の場所に移して別のタイミングで実行させます。
  • ファイル名の昇順に実行されるため、頭に数字等を入れると順序が整理しやすいかもしれません。

01_init.sql の中身の例としては、以下のとおりです。

/*データベースの追加*/
CREATE DATABASE {Spider側のデータベースの名前};
/*ユーザーの追加*/
CREATE USER `{作成したいユーザー名}`@'%' IDENTIFIED BY '{作成したいパスワード名}';
GRANT SELECT, SHOW VIEW ON {作成したデータベース名}.* TO '{作成したユーザー名}'@'%';
/*接続先の追加*/
CREATE OR REPLACE SERVER {サーバー名} FOREIGN DATA WRAPPER mysql OPTIONS(HOST '{リモートのホスト名}', DATABASE '{リモートのDB名}', USER '{リモートのユーザー名}', PASSWORD '{リモートのパスワード名}', PORT 3306);
3. spider.cnf を設置します。

spider.cnf の中身の例は以下のとおりです。適宜ご利用の状況に合わせて書き換えてください。 各パラメータのドキュメントはココにあります

[mysqld]
table_open_cache=6600
max_connections=100
general_log=ON
general_log_file=/var/lib/mysql/mysql-running.log
slow_query_log=ON
slow_query_log_file=/var/lib/mysql/mariadb-slow.log
tmp_table_size=167772160
max_heap_table_size=167772160
open_files_limit=65535
4. Dockerのエントリポイント用のファイルを作成して指定します。

Dockerfile の例では、 mysql_init.sh がそれに当たります。理由と詳細な中身については後述の「ハマったポイント」を参照してください。

5. 最後に、 docker-compose.yml を設置します。

docker-compose.yml の例は以下のとおりです。

version: '2'
services:
  db:
    container_name: container_name
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      MYSQL_ROOT_PASSWORD: test
    ports:
     - "3306:3306"

ハマったポイント

また一方でまだまだ発展途上のエンジンですので、以下のようなエラーも発生してしまいました。 私もまだまだ力不足ゆえ根本解決には至りませんでしたが、回避策を記したいと思います。

1回MySQLが再起動すると、SPIDERでパーティション分割されたDBを読み込めなくなってしまう。

デフォルトのエントリーポイントである/docker-entrypoint.sh mysqldを実行すると、 /docker-entrypoint-initdb.d/ 以下のSQLファイルを実行した後に一回MySQLを再起動してしまいます。

それによって再起動した際に、パーティション分割されたDBを読み込めなくなってしまう問題があるため、/docker-entrypoint-initdb.d/ にてテーブル作成SQLを書いてしまうと、正しく読み込まれなくなってしまう問題があります。

そこで、今回はDockerfileのエントリポイントを以下のファイルにすることで回避しました。(今回の例では mysql_init.sh としております。)

mysql_init.sh の中身の例

#!/bin/bash
/docker-entrypoint.sh mysqld

/etc/init.d/mysql start
mysql -u root -p${MYSQL_ROOT_PASSWORD} -h localhost < /root/scripts/create_tables.sql

while sleep 30; do
   ps aux | grep mysqld | grep -q -v grep
   PROCESS_1_STATUS=$?

  # If the greps above find anything, they exit with 0 status
  # If they are not both 0, then something is wrong
  if [ $PROCESS_1_STATUS -ne 0 ]; then
    echo "One of the processes has already exited."
    exit 1
  fi
done

内容としてはテーブル作成など再起動後に行いたい処理(この例では create_tables.sql)をその後別途行うようにエントリーポイントのファイルを上書きします。

その後はこの例ですと30秒毎にプロセスの死活監視を行い、もしプロセスが終了してしまった場合は停止させます。 こちらの記事が参考になります

また create_tables.sql の中身の例は以下のとおりです。

USE {Spider側のデータベースの名前};
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL DEFAULT 0,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=SPIDER DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='table "users"'
PARTITION BY RANGE (`id`)
(PARTITION `p1` VALUES LESS THAN (200000000) COMMENT = 'server "{サーバー名}"' ENGINE = SPIDER,
 PARTITION `p2` VALUES LESS THAN (300000000) COMMENT = 'server "{サーバー名}"' ENGINE = SPIDER,
...)

{サーバー名} の部分には、先程の 01_init.sql にて CREATE OR REPLACE SERVER コマンドを実行した時に指定した名前が入ります。

以下の条件で、UNION ALLが正常にできないケースがある(カラムがずれる、CASE文を挿入するとエラーが発生するなど)

  • SPIDERエンジンのテーブルに対して、SELECT文に固定値もしくはcase文を入れた場合
  • かつ、partitionを使用していないテーブルに対して実行した場合

これについては、パーティションを必要としない(水平分割していない)テーブルについても、パーティションを用意することで回避しました。

Dockerを使用した効果

Docker化したことによって、課題であったサーバーの立て直しのしづらさと実データとの差異は無事解消することが出来ました。

これは実際にはデータを扱っていないことを活かして、更新する際は単にDockerイメージを自動で一から作り直してDockerコンテナを差し替える運用にした結果、以下のことが容易に出来た要因が大きいと考えます。

  • 実データと構造の差異が発生しても差分を考慮して都度クエリを発行せずに、 create_tables.sql の中身を実データベースの show create tables の内容をベースに生成してDockerイメージを差し替えることで対応できる
  • その結果、別途スクリプトで自動化してDockerイメージを作り直すのと同時に実データベースの変更を反映させることも可能になった。

一般的にはマネージドでないサービスでのデータベースの管理はDockerを使っても大変そうかと思いましたが、こと実データの取り扱いがないSpiderエンジンでは、Dockerと合わせることで想像以上に保守が楽になりました!

【LT会】Akatsuki Geek Live開催レポート!

こんにちは、エンジニア採用担当の花田です。

この度、2/22(金)に学生向けLT会「Akatsuki Geek Live」を開催いたしました!

今回が初めての試みでしたが、30名を超える学生の方に集まっていただき、最高のLT会となりました!!この記事では、その様子を紹介いたします。

 

「Akatsuki Geek Live」とは...

学生エンジニアと、アカツキエンジニアが登壇するLT会です。今回は学生4名、アカツキメンバー4名の計8名が発表しました。その後、参加者全員で懇親会を実施し、交流の場を持ちました。

 

▼詳細はこちらから

aktsk.connpass.com

 

#なぜLT会を開催するに至ったのか

これまで学生の方にアカツキを知ってもらう機会として、会社説明会と座談会を開催していました。座談会での質疑応答は活発ではあったものの、より深くアカツキの技術やエンジニアメンバーについて伝える形にしたいと思っていました。

また、将来エンジニアとして活躍する学生のみなさんに登壇いただくことで、成果発表の機会や、他の学生の取り組みにふれて刺激し合ったり、同じ領域に興味を持っている方とのつながりの場を創ることで、可能性が開花するきっかけとなれば、という想いから企画しました。

 

#当日の様子

初めての開催ということもあり、参加者が集まるか心配しましたが、ふたを開けてみれば2度の増枠に。当日は32名が参加してくれました。また、学年不問で募集したため、4月からアカツキに入社する内定者や、20卒の内定者やインターン生、就職活動を迎える21卒の学生など多岐にわたるメンバーが集まってくれました。登壇順はくじ引きで決め、早速スタートです!

 

▼1人目:@sachaosさん(アカツキ)

「Goの静的解析ツールの作成と活用」

1人目は、アカツキメンバーから!Goによる静的解析のしやすさを発表してくれました。

f:id:kenji-hanada:20190227182712j:plain

発表資料

speakerdeck.com

 

▼2人目:@菱谷さん(アカツキ)

「今風トゥーンシェーディング」

2人目もアカツキから、新卒1年目の菱谷さんが、トゥーンシェーディングへのこだわりを熱く語ってくれました!

f:id:kenji-hanada:20190227183854j:plain

発表資料(speakerdeckには代理で登録しています)

speakerdeck.com

▼3人目:@tkhatotoさん(学生)

「Hack on Slash on Terminal」

3人目は学生枠からtkhatotoさん。会場から「このスライド見ていて楽しい!」と視覚的に楽しませてくれる発表でした!

f:id:kenji-hanada:20190227184515j:plain

発表資料

Coming soon

 

▼4人目:@kawasin73さん(学生)

「俺はビッグエンディアンでテストがしたいんだ!」

3人目は学生枠からkawashin73さん!タイトルからも伝わってくる通り、掴みバッチリのキーワードから熱く想いを叫んでくれました^^

f:id:kenji-hanada:20190227185903j:plain

発表資料(speakerdeckには代理で登録しています)

speakerdeck.com

 

▼5人目:@大嶋さん(アカツキ)

「お父さんが教えるプログラミング教育」

アカツキから、子育て中のパパエンジニアとして、プログラミングの楽しさを伝えたいという想いが詰まっていました!特に「プログラミング的思考を料理で教える」というパートは非常にわかりやすく、参加者の多くがうなずきながら聞き入っていました。

f:id:kenji-hanada:20190227190634j:plain

発表資料(speakerdeckには代理で登録しています)

speakerdeck.com

▼6人目:@july1997さん(学生)

「MMOの作り方」

学生枠july1997さんがMMOの作り方というテーマで、クライアント、サーバ両方の観点から発表してくれました!

f:id:kenji-hanada:20190227192150j:plain

発表資料(speakerdeckには代理で登録しています)

speakerdeck.com

 

▼7人目:@Zepprixさん(アカツキ)

「Docker & ECSで構築するゲームアプリサーバーの話」

アカツキから新卒2年目のZepprixさんの発表。Docker & ECSを活用して管理コストを下げる話をしてくれました!

f:id:kenji-hanada:20190227192457j:plain

発表資料

speakerdeck.com

▼8人目:@acomaguさん(学生)

「RDF/OWLで始める人生イベントソーシング」

トリを務めるのは学生枠よりacomaguさん!RDF/OWLの「ゆるイイ!」理由を熱く語ってくれました。

f:id:kenji-hanada:20190227193151j:plain

発表資料

speakerdeck.com

 

熱のこもった8名の発表はあっという間に終わりました!

今回、登壇いただいた学生の方には、アカツキからプレゼントを贈り、続いて懇親会へ。

発表に刺激を受けたメンバーからの声や、同じ技術領域に興味を持っているメンバー同士での交流など、大いに盛り上がり、会を終えました。

f:id:kenji-hanada:20190228114007j:plain

 

参加者のみなさん、お疲れ様でした!

次回は4/26(金)に開催いたします!今回参加した方も、初めての方も大歓迎ですので、熱い想いを持ったみなさんにお会いできることを楽しみにしています!

 

▼次回予約はこちらから

aktsk.connpass.com

Go用HTTP APIテストコードジェネレータを開発しているというお話

こんにちは、mizzy です。業務委託でアカツキさんのお手伝いをしています。お手伝いの一環として、aktsk/atgen という、Go用HTTP APIテストコードジェネレータを開発しています。

この記事では、Atgenの概要や開発の背景、今後の予定などについてご紹介します。


Atgen とは

HTTP API Test Code Generatorの略で、HTTP APIをテストするためのコードを自動生成する、Go製のコマンドラインツールです。

YAMLで記述されたテストすべきHTTPリクエスト/レスポンスと、Goで書かれたテストコードのテンプレートから、実際にテストを実行するためのコードを生成します。

atgen/example at master · aktsk/atgen に動かすことができるサンプルがありますので、READMEの通りに動かしてみると、どんなツールなのか掴んでいただけるかと思います。


なぜつくっているのか

Goはその性質上、コードが冗長になりがちです。特に、HTTP APIをテストする場合、リクエストを出してレスポンスをチェックする、という同じような記述のテストコードをたくさん書くことになり、更に冗長になってしまいます。

コードが冗長になると、コピペも多くなりますが、コピペした後に修正すべき部分の修正が漏れることもあります。テストが失敗すれば修正漏れに気づくこともできますが、通ってしまうと漏れに気づかず、誤った内容でテストしているがそれに気づけない、ということになりかねません。

また、テストコードが冗長だと、全体の見通しが悪くなり、どのエンドポイントがテストされていて、どのエンドポイントがテストされていないのか、把握するのが難しくなります。

そこで、同じようなコードを書く手間を省き、コピペによるミスを防ぎ、APIエンドポイントのどこまでをカバーできているのかを把握しやすくするために、Atgenを開発することにしました。


開発にあたって検討したこと

このツールが、なぜこのようなつくりになっているのか、はコードを読んでもわからないですし、作者の自分も忘れそうなので、ここに記録しておきます。

YAMLにしたがってテストを実行するのではなく、テストするコードを生成するのはなぜか

ツールの方向性として、YAMLに基づいてテストコードを生成するのではなく、直接テストを実行する、という方向性も考えました。しかし、一度テストコードに落とし込んでから実行することで、テストが失敗した場合に、該当するコードを直接読むことができ、原因を調査しやすいだろうと考え、テストを直接実行するツールではなく、テストコードを生成するツールにしました。

テンプレート処理にtext/templateを使わずにAST書き換えを行っているのはなぜか

AST書き換えをせずにtext/templateを使った方がツール自体の実装は簡単です。しかし、{{ .Var }}のようなtext/template記法がテンプレートコード中に入ると、Go的には不正な構文となるため、go fmt でエラーになるなど、ツールやエディタの言語サポート機能を活かせなくなり、テンプレートコードを書く時ににとても不便です。なので、 text/templateは使わずにAST書き換えで処理をする、という形にしました。

あと、私がAST書き換えを行うようなツールを一度作ってみたいと思っていたから、という理由もあります。

テスト対象のリクエスト/レスポンスの定義にOpenAPI準拠のYAML/JSONを使わないのはなぜか

Atgenを導入しているプロジェクトでは、swagger.yamlでAPIの仕様が定義されているので、これをテストの定義にも使い回せないか、と最初は考えました。しかし、仕様の記述とテストの記述は似ているようで違っていて、特に以下の点が異なると考えています。

  • リクエストやレスポンスのパラメータの違い
    • 仕様ではパラメータの型が定義されるが、具体的な値は定義されない
    • テストでは具体的な値を記述する必要がある
  • コンテキストの有無の違い
    • 仕様では各リクエスト/レスポンスが独立した形で記述される
    • テストでは、このパラメータでPOSTしてから、GETするとこういうパラメータが返ってくる、みたいな形で、複数のリクエスト/レスポンスの関連性を記述する必要がある

このような違いがあるため、OpenAPI準拠のYAMLでテストの定義までカバーするのは無理がありそうだ、と判断して、無理に寄せることはせずに、あえて別のフォーマットにしています。

また、Go用のツールなので、Goの慣習にしたがい、Goの機能を活かしたコードを生成できるようなフォーマットにしたい、という理由もあります。


実戦投入してみての所感

既存プロジェクトのテストコードとほぼ同等なコードをこのツールで生成できるようになったので、生成したテストコードをCircleCIで回すようにしてみました。実践投入してまだ間もないですが、現在のところの所感は以下の通りです。

  • テスト定義をYAMLで記述することで、テスト項目全体の見通しがよくなり、APIエンドポイントのどこまでカバーされているのか、把握しやすくなる。
  • 見通しがよくなることで、レビュアーがレビューしやすくなり、テストの間違いや漏れを見つけやすくなる。
  • 反面、前処理などテストによって微妙に処理が異なるところがあり、ひとつのテンプレートで吸収しようとすると、ifでの条件分岐が多くなり、コードの見通しが悪くなる。
  • 書き換えのためのルールを把握してテンプレートコードを記述しないといけないので、普通にテストコードを書くよりも面倒。
  • このように、テスト項目のメンテナビリティは向上するが、コードのメンテナビリティは低下する。
  • が、テストは書くことよりもメンテナンスすることの方が大変なので、トータルで見ると運用コストを下げてくれそう。
  • テスト項目のメンテナビリティを保ちつつ、コードのメンテナビリティをいかに下げるか、という方向でツールを改善していくのが今後の課題。

今後の予定

まだツールとしては発展途上で、やることはたくさんありますが、以下の様な方向で考えています。

  • ドキュメントの充実
    • 特に書き替えルールがわかりにくいので、その辺りを重点的に。
  • 使い方をわかりやすくする
    • YAMLだけでできる範囲を広げ、できるだけコードを書く量を減らす。
  • OpenAPI準拠YAML/JSONとの連携
    • OpenAPI準拠YAML/JSONとテスト定義YAMLを突き合わせ、テストされてないエンドポイントを出力する、といった機能

または、もっと違う方向性で実装しなおした方が良いのでは、とも考えています。というのも、Atgenは汎用的に使えるようにするため、テスト対象のAPIサーバのコードには一切関知しない、というスタンスで実装しています。ですが、APIサーバのコード内で定義されている型などをうまく利用した方が、よりわかりやすい形でテストコードが書けます。とは言え、テストコードジェネレータが特定のAPIサーバのコードに依存してしまうと、汎用性が失われてしまいます。これを解決するために、テストコードジェネレータではなく、テストコードジェネレータの開発を支援するライブラリとして実装する、といった方向性もあるのではないか、とぼんやりとですが考えています。


謝辞

ツールの実装にあたって、tenntenn さんによる Go AST に関する Qiita 記事motemen さんによる GoのためのGo がとても参考になりました。この場を借りてお礼を申し上げます。