現代のフォローリスト

数日前の話、同僚からサポートissueの調査について相談を受けた。内容は「フォローリストが正しく表示されない(数人足りない)」というものだった。

最初にうちの会社の環境を紹介しておくと、メイン事業はモノリシックで巨大なrailsで書かれている。最近は機能や開発を担当するチームごとに切り崩してマイクロサービス化される事例も増えてきた。今回調査したフォローシステムを提供するアプリケーションもメインのrailsアプリケーションとは別に小さなrailsとして動いていた。

事前調査していた同僚から「DB上のソーシャルグラフは正常だった事」と「名前のないユーザーはフォローリストに表示されない事」を教えてもらった。今回のサポートissueに挙がってきた事例もユーザー名が無いことが原因の様で、それはシステム上意図してない挙動だった。

調査の手始めにユーザー名の管理について調べた。DBはアプリケーション毎に別のものを参照していた。ユーザー名はメインのrailsとフォローシステムのrailsで二重管理されていて、メインのUserモデルに変更があると内製のPub/Subシステムを経由して変更内容が同期されるようになっていた。

Pub/SubシステムはQueueを持っていて、イベントがpublishされるとQueueにどんどん積んでいく。積まれたイベントは非同期にデキューされ、紐付けられたDockerイメージが立ち上がり事前に設定したスクリプトがその中で実行される。

デキュー後の処理はごく稀に失敗することを把握していたので今回もそこが原因だろうと考えた。

ところが影響範囲を調べてみると数十万人(直近数日でも数百人)の規模で起こっていることが判明して読みが外れていることに気づいた。デキュー後の処理が失敗するのは一日数件程度なので、もっと根本的なバグがありそうだった。

調査を再開してUserモデル内の該当イベントがpublishされている箇所を調べた。最近入った何らかの変更でイベントが飛ばなくなったみたいな話はいかにもありそうだ。そこで該当のイベント名でgrepした見たところやはり見当たらなかった。

「おっエンバグの線で当たりかな..」と思ったがこれも間違いだった。Userレコードが書き換わったときに該当イベントのpublishをチェックするユニットテストが見つかった。 もしイベントがpublishされなくなっていたらこのテストケースで気付くことができる。しかしmaster CIはずっとgreenのままだったし、当たり前だけれど手元でテストを実行してももちろん成功した。ここでエンバグによってpublishイベントが消えたという線はなくなった。

やや脇道に逸れるけど一体どんな仕組みで該当イベントをpublishしているのかどうしても気になったので該当モデルのコードをもう少し読んでみた。すると何かのイベントをpublishしている処理を見つけた。注意深くコードを追うとイベント名がメタプログラミングチックに組み立てられていることに気づいた。「なるほどだからさっきのgrepでは引っかからなかったのか」と納得が入った。一方で「なんてgrepビリティが低いコードなんだ。こんなに処理を汎化する意味はあるんだろうか...これだからrubyは..」と悪態をつきたくなった。(もちろんほとんど冗談の文脈。 rubyは別に悪くないし grepしさすさを重視するかは個人の好みに分かれそう)

イベントのpublishまでは正しく行われていることがわかった。次に疑うべきはイベントが流れる経路とsubscribe側の処理なのだけどここは疑う余地がほとんど無かった。他のPub/Subシステムを利用したサービスは正常に動いていたし、さらに言えば内製のPub/SubシステムはほとんどAWSのサービスのラッパーみたいなものでAWS側で障害が起こったりしない限り原因になりえそうに無かったからだ。仮に障害が起こっていたら技術基盤かインフラチームの人がいち早く気づいて共有してくれると思うのでこのあたりを疑うのはすぐにやめた。

こうなってくると手詰まりで「申し訳ないけどちょっと原因がわからないな ちゃんと時間を確保して調べてみないとダメそうですね」という話をした。

他の作業も残っているので一旦調査をやめようかと思っていたのだけど、ふと昨年関わった仕事のことを思い出した。 そういえばユーザー登録基盤はメインのrailsから引き剥がされてマイクロサービス化されていたのだ。

「ユーザー登録基盤のrailsはメインのrailsとDBを共有していて、ユーザー登録基盤のrailsからDBを書き換えるとイベントが発火しないのでは」という仮説が偶然思いついた。結論だけ言うと大体合っていた。正確にはほとんどのテーブルは共有してなかったがusersテーブルのみ直接参照/書き込みしていた。 (あまり詳しくないので間違っているかもしれない)

railsチュートリアルに登場するフォローリストなら中間テーブルから一覧を引いてきて、ユーザー情報を列挙するだけのシンプルな実装で十分だけど、現実のフォローリストには無名のユーザーが表示されないロジックがあったり、ユーザー名はpub/subで渡ってきて登録されていたり、とても複雑な構成になっていた。

たまたま思いついた雑な推測から原因が特定できて問題は解決したのだけど、現代のフォローリストは複雑なんだなーと思ったのが印象的でブログに調査ログを書いてみた。

今回の調査でアプリケーション間でDBの共有すると状況を把握するのが大変になるので、(しょうがないケースも多いけど) 出来るだけ避けたいですね。という学びがあった。アンチパターンなことは知ってはいたけれど、どういうケースで困るのか理解できていなかった。

独学プログラミングとペアプロ

今日のお昼の話。所属部署のとても優秀な学生アルバイトの人とお昼ごはんを食べながら色々と話をしていた。流れは忘れてしまったのだけど彼は今までペアプロを経験したことがないらしいということがわかった。そこからペアプロって具体的に何するんですか?とか色々一般的な説明をして、機会があればぜひペアプロやろうみたいな話をした。

僕はペアプロに良い印象を持っていて、その要因の一つに些末だけど便利なテクニックを同僚から盗める点がある。 覚えている実体験を話すと昔巨大なrailsアプリのスタイルを直している時にちょっとscss変えただけなのに毎回リロードのために5秒待っていたりしたのだけど、ある時デザイナーの人とペアプロしたら彼は秘伝のブックマークレットを持っていて、それを使うと瞬時にスタイルだけ反映することができた。(今でも不思議なんだけどそのブックマークレットを消してしまったので一体どんな仕組みだったのかもう知る術はない)
他にはシェル上でコマンド間違えたりするとバックスペース連打で消したりしていたんだけど、ある時同僚の人とペアプロした時に「cntrl+u でカーソルの前の文字列全部消えるよ」と教えてもらってそれ以来かなり重宝してたりする。(今日ランチしたバイト氏はcntrl+uの存在を知らなかった) なんかそういう細かいけど便利なハックが盗めるので定期的にペアプロしたいなって思っている。

独学プログラミングについても思うところがあったんだけど長くなってしまったのでまた別の機会に書こうかな :P

全仏オープンを支える技術

最近美容院に行く度に刈り上げる領域が増えてきて、最終的にどの部分が残るのか興味があります。それとは別に少しだけテニスの話を書きます。

地上波におけるテニス中継

テニスにはグランドスラムと呼ばれる4大大会があり、多くのテニスファンが毎年楽しみにしています。 グランドスラムが地上波で中継されることは稀です。過去には録画が深夜帯に放映されている時代もありましたが今はなくなってしまいました。(地域によって違いがあるかも)
唯一例外的な大会がウィンブルドンで、ウィンブルドンに限り毎年NHKがある程度の放送時間を確保して中継してくれます。
最近は少し変化があり、錦織選手の劇的な活躍によってウィンブルドン以外でも地上波でライブ中継される機会が増えてきました。 現在行われている全仏オープンテレビ東京が錦織選手の試合を中心に放映しています。特設サイト
テニスファンとして地上波での放映が増え嬉しい半面、錦織選手以外のトッププロ活躍も楽しみにしているので物足りなさがあります。
そこで選択肢にあがるのがWOWOWとの契約になります。

WOWOWによるテニス中継

WOWOWグランドスラムの放映権をすべて取得しているようで4大大会が全てが放送されます。試合解説も専門的で地上波放送にはない魅力の一つになります。
テニス分野にはかなり力を入れているようで、グランドスラム以外にも国別対抗戦であるデビスカップやマスターズの大会が放映されることもあります。
Netflixなどのオンデマンドサービスが増えてきている中で、スポーツのライブ中継などに力を入れるといった背景もあるのでしょうか

WOWOWオンデマンドの付加価値

WOWOWの回線契約をするとWOWOWオンデマンドというwebサービスが利用できるようになります。
海外ドラマや映画のオンデマンドサービスもありますがNetflixなどと比べれば大したことないです。
WOWOWオンデマンドの最大の魅力は大会期間中マルチトラックでLive中継してくれるところにあります。
例えば先日の錦織選手の試合の裏では、ランキング一位のアンディ・マレーとデルポトロというビックカードがありました。
このような場合BS放送では当然錦織選手の試合が優先して放送されてしまうのですが、BS放送とオンデマンドサービスを併用することで両方見ることが可能になります。

我が家の視聴環境

少し前まで大きなテレビがあったのですがメルカリに流してしまったのでMacタブレットを使って視聴してます。

(macがオンデマンドサービスでの視聴, タブレットnasneを経由したBSでの視聴)

今時、衛星放送の有料回線契約をする人は稀有だと思いますが、WOWOWも結構便利だよという話をしたくてブログに書きました。

danger-findbugsを作った

Dangerについて - kazy no blog

最近dangerに興味があり、なにかプラグインを作ってみたかったので danger-findbugsを作った。dangerの人気にあやかってstarを集めたい気持ちです。

github.com

下記のような設定をDangerfileに書けば、findbugsから受け取ったバグ情報をgithubのPR上にコードコメントすることが出来る。

findbugs.gradle_module = "prefkit"
findbugs.gradle_task = "prefkit:findbugs"
findbugs.report_file = "prefkit/build/reports/findbugs/findbugs.xml"
findbugs.report

難しいことはしてないし、一応動作確認はしているので動くと思いますが、なにか問題があったらisuueで報告してください。🙏

f:id:kazy1991:20170514211717p:plain

Dangerについて

Dangerが気になって、先週末に少し調べました。以下はその時書いたものです。

Dangerとは

Dangerはコードレビューbotで、最近人気を集めています。似たようなものにcookpaddokumiがあります。小さなチームおけるコードレビューの負荷はそれほど大きくないですが、 メンバーが増えてくるとレビュー作業は多くの時間を使い、消耗の度合いが増してきます。(個人の感想です)
レビュー作業は機械的に解決できることも多いため、そのあたりはbotに任せて人間はより重要なレビューに時間を使おうというのが利用するモチベーションだと思います。

特徴

http://danger.systems/

DangerはRuby製のプロジェクトです。DangerfileというファイルにDSLで設定を記述しプロジェクトに含めることで動かすことが出来ます。Ruby製ですが、対象とするプロジェクトの言語はなんでも良いです。CLIからコマンドが実行できて、出力が受け取れれば、任意のプラグインを作ることが出来ます。コアは小さく、様々な機能はプラグインによって提供されています。プラグインはそれほど揃ってないですが、見方を変えればチャンスと考える事もできます。iOS環境が比較的充実している印象で、Android関連ではAndroidLintとJUnitプラグインがあります。

導入

http://danger.systems/guides/getting_started.html

danger init を実行するとセットアップをサポートしてくれます。dangerはbundlerの利用が推奨されているので、以下のようなGemfileを用意します。

# frozen_string_literal: true
source "https://rubygems.org"

gem 'danger'
bundle install
bundle exec danger init

コマンドを実行するとひたすら長文のメッセージが流れます。読んだらenterで進みます。
Step 1はDangerFileを作ったよ、Step2はbot用のクールなgithubアカウントを作ってくれと言われます。重要なのはイケてるアイコン画像の設定を忘れないことと書かれています。creative commonsな画像リンクが表示されるのは良いですね。早速作りました。https://github.com/kazybot
Step 3で、botアカウントでアクセストークンを作るように言われます。Publicなレポジトリの場合 public_repo の権限だけで問題ないそうです。
Step 4でCI側の設定を求められますが、詳しい案内はないので、setting-up-danger-to-run-on-your-ciを見ると良さそうです。
全てのセットアップが完了したら、CIからbundle exec dangerを実行します。

Wercker対応されてない問題

残念なことに2017年5月地点ではWerckerはdangerにサポートされていません。dangerは対象のPRのidを取得するためにCIサービスが提供する環境変数を利用しています。そのため各環境に対応したスクリプトが用意されているのですが、このリストにWerckerは含まれていないためです。
Wercker対応のPRを投げようと考えたのですが一筋縄では行きません。というのも大抵のCIサービスは、PRのidを環境変数で提供していますが、Werckerにはそれが存在しないためです。一応アドホックな対応として他のCIに偽装し、githubのURLリダイレクト処理を利用することでwerckerでも動かす方法は考えたのでよければ参考にしてください。気が向いたらdangerにwercker対応のPRを送ってみようと思っていますがもっと良い方法無いですかね。。🤔

最近の進捗(2017-4-19)

最近の進捗

今年に入ってからいくつかライブラリを作っている。完成したらQiitaやこのblogにも紹介記事を書きたいと思っているのだけど、 なかなか他人におすすめできるレベルに持っていけない。なのでどんなものを作っているかだけでも紹介しようと思っている。

PrefKit

github.com

(進捗度: 70%)
これはSharedPrefのwrapperライブラリで、冗長な記述を省けるように設計した。使い方は以下の形式のInterfaceを用意するだけになっている。

@PrefSchema("SampleSchema")
interface SampleSchema {

    String WELCOME_DIALOG = "welcome_dialog";

    @PrefKey(WELCOME_DIALOG)
    void putWelcomeDialogFlag(boolean value);

    @PrefKey(WELCOME_DIALOG)
    boolean getWelcomeDialogFlag(boolean defaultValue);
}

このInterfaceをPrefkitというインスタンスに渡すと実装が返ってくる

PrefKit prefKit = new PrefKit(this);
SampleSchema sampleSchema = prefKit.create(SampleSchema.class);

作った背景

Retrofitの実装が好きでAPIClient意外に応用出来ないかなと考え試しに作ってみた。 他にはKVS-Schemaというライブラリにも影響を受けている。KVS-SchemaがAnnotationProcessingを使っているのに対して、こちらはリフレクションベースの実装になっている。 AnnotationProcessingを過度に利用する最近の雰囲気が嫌で小さな抵抗のつもりもある。

PrefEditor

github.com

(進捗度: 45%)
先週末ガッと作り始めたもの。
これはデバッグツールで、アプリ内のSharedPrefを変更できるActivityを提供している。基本機能は出来てるんだけどUIがまだ定まっていない。 今のところ一番モチベーションがあるので来週あたりには使ってもらえる形で提供したい。

作った背景

Macにつながっている環境だとSthethoでPrefの中身を覗いたり、書き換えたりするのだけどアプリ単体で出来ないことに課題を感じていた。 初回起動にのみ表示されるダイアログとか作ってると、確認作業がひたすら面倒なことがあって、デザイナーさんなど自分以外が確認する時に便利な仕組みがほしいと思っていた。

robotkit

github.com

(進捗度: 65%)
Android用ライブラリ・テンプレートジェネレーター

作った背景

上で紹介したもの以外にも10個くらいAndroidライブラリ作っているのだけど、初めにやるテンプレ作業が嫌で自動化した。robotkitについては一度このブログにも書いた。 gem install robotkit で入るのでぜひ使ってみてほしい。robotkitと打てばなんとなく使い方がわかるようになっている。

おわり

他にも作りたいものは結構あって例えばDependencyKit って名前の小さなDIコンテナライブラリも作っている。昔作ったライブラリで130starくらいのレポジトリはあるんだけど、今年はそれを超える500star 1000starあたりのライブラリを作っていきたい。

Android OSSライブラリのCI環境をセットアップする

ほぼ初めてAndroid向けのCI環境をセットアップしたので、試行錯誤した作業手順を書いておこうと思う。
今回セットしたCIの内容は、JVMテストとカバレッジの計測で作業手順は以下のようになった。

続きを読む