Gunosy Tech Blog

Gunosyの開発メンバーが知見を共有するブログです。

Gunosy広告配信を支えるGo ~キャッシュ編~

広告技術部の今川です。 わたしは主にGoを使って広告配信APIの改修を担当しています。 今回はAPIの高速化のためにキャッシュを使った話をご紹介します。

データベースへのアクセス頻度を減らすための努力

広告技術部では、広告配信APIのCTRの予測で利用する特徴ベクトルをAmazon Auroraに保存しています。 しかし、単純にデータ取得時に毎回Auroraに問い合わせていては処理速度が遅くなってしまうので、以下の二種類の方法で高速化を図っています。

1. キャッシュする

予測ロジックで利用するデータの中でも、ユーザーに関するベクトルはユーザーIDをキーにユーザー数だけのデータが存在する反面、すべてのユーザーが同じ時間帯にアクセスするとは限らず、一度訪問したユーザーが続けて何回もアクセスする場合が多いという特徴があります。

このため、利用したいデータがキャッシュに無ければAuroraに問い合わせ、取得済みデータは一定期間キャッシュする方針を採りました。 また、最初にmemcacheにキャッシュした後、APIサーバーのメモリにも(go-cacheを使用して)キャッシュするという二段階キャッシュを行っています。

f:id:imagawa_yakata:20171024170001p:plain

こうすることで、ひとつのサーバーが同一ユーザーのリクエストを続けて受ける場合はインメモリキャッシュが効くだけでなく、複数のサーバーが同じユーザーに対するリクエストを受けてもmemcacheにデータがあるのでAuroraに問い合わせる頻度を減らすことができます。

2. 起動時にすべてのデータを読み込んで定期更新する

広告のベクトルは比較的データ量が少なく、ユーザーをまたいで同じデータに頻繁にアクセスするため、キャッシュではなく起動時にすべてのデータを読み込んでしまう方針を採りました。 但し、データは一定間隔で最新状態に更新されるので、サーバー起動後に定期的にデータを入れ替える必要があります。

実装はシンプルにGoのmapとsync.RWMutexを使って行っています。(以下イメージ)

  • 予測ロジックのリクエストが来たら、RWMutexのRLock/RUnlockでロックを取って広告ベクトルを取得する。(この間、他のgoroutineは広告ベクトルの読み取りは可能だが、書き込みができない)
type AdVector struct {
    db      *sql.DB
    data    map[int32]string
    mu      sync.RWMutex
}

func (adv *AdVector) Find(id int32) (string, error) {
    adv.mu.RLock()
    defer adv.mu.RUnlock()
    if s, ok := adv.data[id]; ok {
        return s, nil
    } else {
        return "", fmt.Errorf("not found id=%d", id)
    }
}
  • メインルーチンとは別に自動更新用のgoroutineを動かし、一定間隔で最新データを取得しmapを入れ替える。この場合はRWMutexのLock/Unlockでロックを取る。(入れ替え中、他のgoroutineは読み取りも書き込みもできない)
// このメソッドは別のgoroutineで動かす
func (adv *AdVector) AutoUpdate() {
    ticker := time.NewTicker(30 * time.Second)
    for {
        select {
        case <-ticker.C:
            // エラーが起きたら無視して稼働し続ける
            if err := adv.Load(); err != nil {
                log.Printf("load error %v", err)
            }
        }
    }
}

func (adv *AdVector) Load() error {
    m, err := selectVector(adv.db) // Auroraからデータ取得
    if err != nil {
        return err
    }

    adv.mu.Lock()
    adv.data = m
    adv.mu.Unlock()
    return nil
}

これで、データの入れ替え中だけ予測ロジックのデータアクセスを待ち、ロック待ち時間を抑えられました。

データの特性を考慮した設計が必要

高速な広告配信を実現するために、Gunosyの広告配信APIでは上記のキャッシュ戦略を取っています。

しかし、単にキャッシュすれば速くなるわけではなく、データの特性を考慮せずキャッシュを利用するとむしろ遅くなったりメモリを圧迫するボトルネックになります。

例えば、1の方針を長期間アクセスし続けるデータに適用すれば、キャッシュからデータを消せず次第にキャッシュの容量が不足してきます。これはキャッシュの生存期間を必要以上に長く設定した場合でも同じです。

キャッシュはAPIを高速化するために役立ちますが、同時に別の難しい問題をもたらすのでデータの特性に合わせた設計が必要です。

最後に、Gunosy広告技術部ではパフォーマンスチューニングの腕に自信のある人を募集しています。自分ならもっと速くする方法を知っている・速くしたいという方、ぜひ応募お願いいたします。

hrmos.co

参考リンク

qiita.com

チームの生産性を上げるために新規事業チームがやっていること

f:id:u_tis:20171016112521p:plain

こんにちは、新規事業開発室のエンジニアの@__timakin__です。上記は生産性が全く上がっていない様子です。

forkwell_meetupというイベントにところで「チームの生産性を上げる的な話をしてください」というフリをされたので、偉そうな話をして来ました。

forkwell.connpass.com

発表について

スライドは以下の通りです。

speakerdeck.com

冒頭の方に実際のSlackのチャンネルやカレンダーの様子を載せましたが、だいぶフランクにやりとりが行われています。

新規事業へもともと興味がある人間が集まると、そこまで強制しなくてもリサーチする流れが生まれますし、逆に言えば採用の段階で新規事業の立ち上げコストというのは決まってくるのだなと思います。

生産性とは

生産性とはなんぞやという話だったのですが、僕個人として言いたいのは「生産総量ではなくて投資効率性ですよね」という話です。

単純な量より質だよとよく言われますが、人は単純にOutputがよく出ていると生産性が高いと感じてしまいます。

しかし、そのOutputにどれくらいのコストがクリアにならないと、単なる生産総量しかわからず、生産性が高いかどうかはわかりません。

Input改善の余地が薄くて、Outputの増加に全力を注ぐ時に気合を重視するのは良いのですが、改善の余地が少ない場面などほとんどないので、素直に生産性、すなわち投資効率性をKPIにおいたほうがいいと思います。

まとめ

生産性とはOutput/Inputというシンプルなもののはずなのですが、いつのまにかその意味を取り違えたり、Outputのみを追ってしまったりしてしまうことが多々あります。

我々も模索過程ではありますが、生産性が大事だよねという考えは根付いていますし、それを数字によって裏付ける体制が整っています。

そういう考え方なら、生産量だけを追い求めて残業続きになるとか、単純に時間投資が多い人と効率が高い人を同じ目線で比べてしまうなどの人事的な問題も解決されるのではないかと思います。

最後に、我々Gunosy新規事業開発室ではスライドに書いたような体制で和気藹々と進めており、全くギスギスした空気や理にかなわない意思決定というのはありません。ぜひ同じチームで一緒に働きましょう。

www.wantedly.com

www.wantedly.com

droidcon NYC 2017に行ってAndroidの最新開発事情について調査してきました!

f:id:shunyy:20171012182119p:plainはじめまして、グノシー開発部新卒エンジニアの山本です。 主にグノシーのAndroid版や、最近リリースしたLucraのAndroid版を作ったり、サーバーサイドでGo/Pythonで開発をしたりしています。

少し時間が空いてしまいましたが、9月25~26日にニューヨークで開催されたdroidconNYC 2017に会社に費用を出してもらって参加してきました。 今回はそのレポートを書いていこうと思います。

droidconについて

f:id:shunyy:20171011205100j:plain

droidconとは世界各地で行われるAndroid専用のカンファレンスで、Android専用でいえば世界でも最大規模のカンファレンスです。 ニューヨークの他にもロンドンやベルリン等(日本はまだ😢)で行われ、終了後には各サービスでスライド等が盛り上がるので知ってる人も多いかと思います。

今回のdroidcon NYC 2017では70以上のセッションが行われ、900人以上のandroidエンジニアが参加しました。 AndroidエンジニアにはおなじみのJakeも参加するなどAndroid業界の祭り感があります。

上の写真は会場の入り口です、おしゃれな雰囲気が出ていると思います。

会場

会場はタイムズスクエアの角のところを奥に入ったところというかなりニューヨークな場所でした。

f:id:shunyy:20171011205256j:plain 朝ごはんが出たり

f:id:shunyy:20171011205433j:plain コーラやコーヒーなどが飲み放題だったり

f:id:shunyy:20171011205455j:plain 可愛いグッズが配られたりしていました。 Wifiも速いしコンセントも豊富にあって言うことないですね 😂

f:id:shunyy:20171012114141j:plain これはただの夕食ですね 😂

セッション

トータルのセッション数としては70以上ありますが、同時に4セッションが行われるので実際に見れたのは20セッションほどでした。

すべてのセッションの動画は基本的にはすべてYouTubeのチャンネルにアップロードされるので(今月末とかだと思います)そちらで見るか、スライドだけで良い方はこちらにまとまっているのでそちらでチェックすることができます。

以下では僕が参加できたセッションの内、気になったトピック毎に抜粋して載せていこうと思います。

全体の印象

全体の印象としては一定以上の規模のアプリでは

  • RxJava
  • Dagger 2
  • Kotlin

はもうデフォルトで採用されるものとして定着しているように感じました。

数年前はDagger(kotlin)入れるとこんないいことがあるよ的なセッションがありましたが今回はなく、当然のようにスライドのコードがkotlinであったりとか、Daggerのパフォーマンス改善といったより発展したセッションが複数ありました。

グノシーでも上記のライブラリ(言語)はすべて使っていますが、今となっては無い生活は考えられないのでそういうことなんだと思います。

アーキテクチャ

カンファレンスといえばアーキテクチャの話といったイメージがあるぐらい一向に落ち着く気配を見せないアーキテクチャ議論ですが今回も数多くのセッションがありました。 既存の比較的古いといってもいいMVC, MVP, (素直な)MVVMのアーキテクチャでは非同期処理や状態保持の方法が実装者に委ねられており可読性に問題があることが多いという課題感はどこも持っているようでした。

MVI

MVIに関するセッションが複数あり、上記の課題感の解決策としての盛り上がりを感じました。 MVIとはWebでいうRedux(Flux)のような一方向データフローをAndroidでも実現しようというアーキテクチャです。

非同期処理による状態遷移の難しさですが、具体的には

  • サーバー側からの完了通知やユーザー入力等、イベントが複数スレッドから呼ばれた場合の状態遷移の難しさ
    • 状態遷移を単一スレッドに制限したり排他制御を自分で書くのが難しい
  • (素直な)MVPだと状態をViewとPresenterどちらも持ててしまうので、どちらで持つのか問題、またその整合性
    • AndroidのViewが絡むのでだるい
    • これはMVVMでも解決しようとしていますが

からくると思いますが、MVIはそれらの問題を

  • ユーザー入力やAPIの完了通知を一つのストリームに流し、状態遷移はただの関数であるreducerのみで行う
  • View側は状態を一切持たずに変更通知のみを受け取り、変更後の状態のみによって表示を変更する

といった制約をもたせることによって解決しています。 この解法はRedux(Flux)の概念と非常に似ており、いろいろあったJavaScript業界で、ある程度完成された方法をAndroidにも持ってきた形なので多少の安心感があります。

また、ユーザー入力や完了通知のストリームをRxJavaで表現する事によって可読性の高いコードになっています。RxJavaでのココらへんの状態遷移の書き方はJake(また)さんの以下のスライドがわかりやすいです。

今回のdroidconの Model View Intent, Embracing Reactive UI's では、実際のMVIを用いたアプリのコードがgithub上に公開されているので参考にしてグノシーのコードも書き換えていければなと思いました。

Viewパフォーマンス

これもどのカンファレンスでも多いトピックですがViewのパフォーマンス周りのセッションもかなり多かったです。 今年はFacebookから登壇される方が多く、Lithoに関する発表を開発者から直接聞けたのはいい経験でした。

Litho

f:id:shunyy:20171011213318p:plain LithoとはFacebookがオープンソースで開発しているUIフレームワーク(レイアウト+レンダリング)で、以下の特徴があります。

  • 非同期レイアウト
    • 通常のViewのサイズを測ったりレイアウトを決める処理はメインスレッド上で行われますが、複雑なレイアウトだとレイアウトに時間がかかりスクロールがカクついたりといったことが起きがちですが、Lithoはこれらの処理をバックグラウンドスレッドで行うためユーザー体験を損なわない
  • よりフラットなView階層
    • ネストの深いViewはレイアウトが重くなりがちですが、LithoだとすべてのViewは一つのView内にレイアウトされるため軽い
  • テキスト描画の最適化
    • TextViewはかなり重いViewですがLithoを使うとTextViewを使わずに描画するので軽い
  • リサイクルの最適化
    • RecyclerViewだと行毎のリサイクルですが、LithoはリストではなくてもすべてのViewをより細かい単位でリサイクルします(1テキスト、1画像単位でリサイクルされる)

このリストだけ見るとAndroidエンジニア歓喜という感じですが、レイアウトをコードで書く必要があったりでそれなりに移行コストは高くなっています。ただ、LithoをFacebookのタイムラインのレイアウトに使うとレンダリング(measure,layout,draw)にかかる時間が1/10になったという話をしていたので乗り換える価値はありそうです。

ConstraintLayoutの登場でViewのネスト問題は改善されてきていますが、TextViewやImageViewなどのそもそもViewが重い問題は永遠に残り続けるのでCustomViewで気合で乗り切るのかLithoのような仕組みを取り込んでいくのかは検討していきたいですね。

FacebookはReactNativeだと思っていたんですが、どういう住み分けなのかも気になりますね。

名前はStethoとかけてるの…?🤔

オフライン対応

オフライン対応は意外とセッションが多かったです。 こちらもFacebookの方からの発表がほとんどでしたが、Facebookのように電波の不安定な環境で使われることが多いアプリ以外でもユーザー体験向上のためにオフライン対応を進めているアプリが多いようでした。

ネットワークがないとエラーを出すアプリが多い中、オフラインはエラーじゃなくてステータスだからエラーで甘えるな的なことをおっしゃっていて、すいませんでしたという感じでした。

こちらはグノシーでも進めていきたいところなのでまた詳しくブログに書いていこうと思います。

まとめ

他にもRoomとSQL Delight(どちらもSQL用ライブラリ)の開発者によるどちらのライブラリがいいか直接対決があったり、Android thingsのハンズオンでTensorFlowを用いた物体認識など楽しいセッションが数多くあり常にワクワク感のある非常に良いカンファレンスでしたが長くなりそうなのでこの辺にします。

これまでカンファレンスにはあまり行ったことがなかったのですが、海外の優秀なAndroidエンジニアの方とふれあい、自分に足りないものや目指すものが見えたことは非常にいい経験でした。

初海外で単独ニューヨークというかなり緊張感のあるカンファレンスでしたが、無事得るものは得て帰ってこれたのはよかったです。次に行く機会があれば、もうすこし英語で会話がスムーズにできるようになりたいですね(ほんとにこれは苦労した)。

加えて

GunosyではAndroidのアーキテクチャやパフォーマンスに興味があったり、droidconに行きたいAndroidエンジニアを募集しています! 皆様のエントリーをお待ちしております!

hrmos.co

blockchain.tokyo#1を開催しました。

f:id:ikutyy:20171006111444p:plain

はじめまして、新規事業開発室の生田です。 10/3(火)に開催したblockchain.tokyo#1について紹介します。 blockchain.tokyoは主に仮想通貨/ブロックチェーン/ICO関連の技術に関する発表を中心に行う勉強会です。

blockchain-tokyo.connpass.com

会場について

今回は、弊社のスキップヒルを使って実施しました。 天気がいい日には、バックに東京タワーが綺麗に見える開放感のあるスペースで、普段は全社の朝会などに利用しています。

f:id:ikutyy:20171006113456j:plain

発表について

Payment Channel Introduction

弊社新規事業開発室の@mosa_siruより、送金確定までに時間がかかるというブロックチェーンが抱えている課題を解決するPayment Channelに関する発表です。

www.slideshare.net

いくつかの技術の組み合わせでトラストレス(≒詐欺にあわない)な実装が実現していることに衝撃を受けました。反対に、中央集権型の取引所などはトラストレスな仕組みを敷いていないがお金が返ってくる、と過信している部分もあるという気づきを得ました...。

動かして学ぶFull Node + Wallet + Payment Channel

@_achikuさんは同じくPayment Channelに関する内容でしたが、こちらは実際にコマンドライン上でtestnetを動かしながら発表をして頂きました。

github.com

まずスライドを使わないという発表形式に驚きましたが、でもシンプルで分かりやすい(作るのも楽そう)発表でした。どのように動いているのか、実際のデータはどのような構造になっているのか見えるとイメージができるので分かりやすかったです。私も自分のPCでビットコインをtestnetで動かしてみて、ビットコインの理解がかなり進みました。

HyperLedger Today

@mosa_shiruに続いて新規事業開発室の@timakinがIBMやIntelなど大手IT系企業や金融系の企業が多数集まって開発しているHyper Ledgerについて発表を行いました。

speakerdeck.com

ブロックチェーンといえばビットコインやイーサリアムを支える技術というイメージが強かったのですが、それ以外のプロダクトに適用される場合にどうなるのかイメージが湧く発表でした。また、Irohaというブロックチェーンのスタートアップが日本にあることも初めて知りました。

分散型取引所の現状

最後に@zabeth129さんより、分散型取引所の現状についてお話して頂きました。

www.slideshare.net

取引所といえばbitFlyerやCoincheckというイメージが強く、分散型取引所は名前だけで実態はほとんど知りませんでした。中央集権型が取引所の主流になっていますが、規制の影響等で分散型の需要も一定程度出てきそうな予感がしました。あとcoinboardすごく便利で、さっそく登録して使ってます。

全体を通して

どれも、実際に手元で動かす/具体例を交えるなど分かりやすい発表だったため、基礎知識+αの私でもなんとか話についていくことができました。特に技術の話は仮想通貨/ブロックチェーンが今後どのように発展し利用されていくのか、イメージが湧いてくるような発表ばかりでした。

最後に

新規事業開発室では現在エンジニアを募集しております!(その他、様々な部署・職種も募集しています。) 仮想通貨/ブロックチェーン/音声デバイスなど新しい技術やテーマで事業を立ち上げていきたい方がいらっしゃいましたら、ぜひご応募ください。

hrmos.co

DevFest Tokyo 2017で「Blockchain on Go」というタイトルで発表しました。

f:id:u_tis:20171010133535p:plain

こんにちは、新規事業開発室のエンジニアの高橋(@__timakin__)です。

今回はDevFest Tokyo 2017というイベントに登壇者として行ってきました。

「golang.tokyo」枠で参加してきまして、一応Go関連の話をするということだったのですが、Goのコードの話はせず、Goを使っているブロックチェーン関連のプロジェクトの話をしました。

発表について

スライドは以下です。

www.slideshare.net

発表の冒頭でブロックチェーンそもそもの概要を説明して、そのあとBlockchainとGoの関係について話す、という流れでした。

ブロックチェーンの概要部分について

個人的にはHyperledgerというのが好きなのですが、やはりEthereumが界隈では推されているんでしょうか。

極力その場で試せるようなWebUIやサービスを貼り付けておいたのですが、中でもblockchaindemo.ioは最高だと思ってます。

blockchaindemo.io

Goとブロックチェーンについて

Goに関しては賛否両論ありますが、僕は個人的に読みやすいし速いしユーザー及び開発者利益に貢献するので最高だと思ってます。

ブロックチェーンの開発にGoがよく用いられる理由には、インターフェース定義による理解の容易さやフォーマット、prof、クロスコンパイルなどなど様々な利点があると思いますが、正直「総合して良い!」という感じなので、これがクリティカルだからGoがよく使われる、というのはない気がしてます。

一方で、自然とGoを使ってもらえる、しかもgethなどの大規模な採用実績があるのは強みですし、なかなかオープンにならないGoのAPI実装がブロックチェーン界隈から出ると、Goコミュニティのノウハウ蓄積にも繋がる良い流れだなと思います。

DevFestについて

今回が初参加のDevFestでした。テーマが「ボーダーレス」ということだったので、あまり他の方が触れないであろうテーマを投入していこうと思い、ブロックチェーンを選びました。結果的には正解だったと思います。

一方で、GCP、Androidをメインに据えつつ、VR, IoTなど気になるテーマを広範に取り上げていたため、カンファレンスとしては参加しがいがありました。

また、運営の方が各コミュニティ主催陣というのもあって、安定度が違いました。

参加者としては運営安定度が高く、テーマが面白いカンファレンスは理想的なので、ぜひ次回も参加したいと思います。

加えて

今回はブロックチェーンのことについて話しましたが、自分がいる新規事業開発室では、次に来そうだと思う分野のリサーチなどを各自自主的に行なっております。

新規分野の開拓に興味があったり、その分野の先導をしたいという意欲がある方がいらっしゃいましたら、ぜひぜひ一緒に働きましょう!学生の方でも大歓迎です。

hrmos.co

hrmos.co

hrmos.co