Spotifyのバックエンドの記事のまとめ
Spotifyの公式ブログや、登壇した動画、記事を中心に読んで面白かったものを簡単にまとめた。全部は読めてないが、バックエンドのものを中心に読んだ。
モチベーションとしては、「なぜSpotifyは遅延なくスムーズに再生できるのか」を知りたかったが、探したところ肝心な部分は記載されてなさそうだった。
今回は記載してないが、エンジニアが増えてもスケールするための文化や、そのためのインフラや設計などについての記事も多く、他の企業よりも成長に注力している印象を受けた。
Creative usernames and Spotify account hijacking
Usernameにunicodeをサポートしたことで、アカウントがハイジャックされるバグが発生した。
Usernameは大文字小文字などを区別しないようにした方が紛らわしくなくなるので、望ましい。しかし、それだけでは不十分で、Ω(ギリシャ文字)とΩ(電気の単位)もややこしいので、区別すべき。
これは、XMPPのnodeprep canonicalization メソッドがサポートしているのでそれが使える。
このメソッドがすべてのunicode文字列をサポートできておらず、この関数に冪等性が保証されておらず、複数回実行する必要があった。
最終的には2回実行した結果と1回実行した結果が異なる場合は、許可しないことで解決した。
How to shuffle songs?
Fisher–Yates shuffleというアルゴリズムを使って、完全にランダムなシャッフルな実装を行っていた。しかし、ユーザの問い合わせから、ギャンブラーの誤謬などのように人間がランダムに感じにくいということに気づいた。
ユーザが期待するランダムは、アーティストなどのグループごとに全体の中で均等な間隔でシャッフルされることである。例えば、100曲あるプレイリストの中であるアーティストの曲が3曲があるとすると、1-33, 34-66, 67-100のそれぞれに1回ずつ再生され、かつそれぞれの間で適度な隔たりがあるように(例えば1, 66,67などは避ける)ことが期待されている。
そこで、上図のように各アーティストごとにFisher-Yates shuffleでランダムにシャッフルして、それぞれの間隔を決めたあとに、合成するようなアルゴリズムにした。アーティスト間で1番目の順番が重複しないように、各アーティストごとにoffsetを設定した。
Fisher–Yates shuffle
有限な集合からランダムな順列を生成するアルゴリズム。
1-Nの数字を記載して、それをランダムにピックアップして並べることで実現される。
これの改良版であるAlgorithm Pは、以下のようにランダム化されてない部分(A)とされた部分(B)に分けて、Aの最後尾の要素と、Aの中からランダムにピックアップしたものを入れ替え、Bの先頭に加えることで、ランダムな順列を生成する。計算量がO(N)になる。
array = (1..8).to_a (array.length - 1).downto(0) do |i| j = rand(i) array[i], array[j] = array[j], array[i] end
Floyd–Steinberg dithering
画像処理でよく使われるアルゴリズムで、例えば最大256色までしか使えないGIF形式への変換の際に使われている。
Switching user database on a running system
ダウンタイムなしで、PostgreからCassandraにどのように切り替えたかについての記事。
Postgreを使っていたが、readのスケーラビリティに限界が来た。更に、writeはLondonのデータセンターにあるmasterのみで行っていたので、ネットワークの問題が発生するとSPOFとなる問題を抱えていた。
そこで、Cassandraにダウンタイムなしで切り替えることに。
完全に切り替える前にはdarkloadとしてCassandraを稼働させておくことで、バグの発見等を行った。
実際に切り替える際には、users DBにアクセスするのはRESTful HTTPリクエストでラップすることで、切り替えなどのロジックを知るサービスを1つにした。そして切り替えの際にはconfigを更新して、PostgreとCassandraが並行でmasterとして稼働するのを避けるために全サービスインスタンスを同時にリスタートさせた。
ELS: latency based load balancer, part 1
LBのロジックはいくつかある。ラウンドロビンは、重いリクエストが集中したインスタンスがあった場合にレイテンシーが悪化してしまう。Join the Shortest Queueでは、ラウンドロビンの問題が解決できるが、エラーを高速に返すインスタンスが存在した場合などに、全体のエラー率が高くなってしまう。
Expected Latency Selector (ELS)
これらの問題を解決するために、ELSを開発した。これは確率的なLBで、各マシンが重さを持ち、その比重になるようにリクエストを振り分ける。ELSは以下のような項目を計測する。
Relative circuit breaker
失敗の割合がインスタンス全体の平均より悪い場合のみに、サーキットブレーカーを発動させるように設定できるように改善させた。全体のエラー率が高い場合は発動させないなど。
Pros and cons of ELS
LIMITATIONS
- LBのローカルのメトリクスのみ使用
- 99パーセンタイルのレイテンシーの方がユーザにとって重要なので、それに基づいて重み付けを行える
- 遅いマシンを追加するとパフォーマンス悪化の原因になる(常にいくらかのリクエストを受けるため)
- Not invented here syndrome? Perhaps, but C3: Cutting Tail Latency in Cloud Data Stores via Adaptive Replica Selection uses almost the same approach for Cassandra.
ADVANTAGES
- 素晴らしパフォーマンス: JSQと比べて、エラーがない場合はほとんど同じ、マシンがダウンしている場合は傑出したベンチマーク
- ローカルで計測できるメトリクスのみ使用するため、コードがシンプルでエラーを抑えられる
- Relative circuit breakerによって、パフォーマンスが平均に比べ著しく悪いときのみ発動できる
- ローカルで計測できるメトリクスのみを使用するため、100以上あるSpotifyのバックエンドサービスの修正が不要。
- 半年運用してきたので、この技術の信頼性が高い
Backend-driven native UIs
www.slideshare.net
APIにViewModelのような情報を返させることで、UI変更を簡単に行えるようにした。
The Brilliance of Spotify Internal APIs to Mitigate Payments | Nordic APIs
決済のAPI設計に関する記事。
グローバルで展開すると、世界中の通貨や、地域特有の決済方法に対応しないといけない。
そこで、拡張しやすいように、決済のバックエンドをCheckoutとBillingのAPIで切り分けた。
The Checkout API
- デバイスごとに料金が異なったり、国によって税金が異なったりする
- 適切なフォームを表示して、オファーコードの有無や、クライアントのデバイス情報、国の情報を収集する
- バックエンドが次の操作をクライアントに対して指示する
The Billing API
- バックエンドとPayment Providersの橋渡しになるAPI
- 16ものPayment Providersごとに異なるビジネスロジックを隠蔽する
- 16 different Payment Providers
- モニタリングも行いやすくなる
4 Reasons Why API Design is Critical to Subscription Services
- 支払いプラットフォームから独立したAPIを提供できる
- 内部でのインテグレーション: 会計プラットフォームやセキュリティ、モニタリングなどの連携が行いやすくなる
- 成長のためのスケーリング: 新しいビジネスロジックとの互換性をサポートしやすくなる
- カスタマイズの可能性
Scalable User Privacy
ユーザの個人情報をセキュアにかつスケーラブルに扱うために、どのような設計にしたかという記事。
Why we encrypt
各ユーザごとに暗号キーを使っている。そうすることで、データが漏洩しても無意味なものにできる。
また、中央集約的に各ユーザのデータのライフサイクルをコントロールできる。データの削除する際には、暗号キーを破棄するだけで、データが参照できなくなるため、削除漏れを防ぐことができる。
データ保存する際には暗号化させるというシンプルなルールだけで、各チームは設計を比較的自由に行い、プライバシーポリシーの標準を守ることができる。
考えていた他の案
- 一つのサービスが他のすべてのサービスのユーザ情報を削除するAPIを呼ぶ方法
- サービス数が多いと、すべてのシステムで正常に処理されたかどうかの保証が難しい
- We want to keep our datasets immutable, because scanning through petabytes of data every time we want to delete a few rows for a single user is too expensive.
- 一つのDBのみに保存し、サービスはそのDBのキーとなるトークンを保持する
- 様々なサービスの要件を満たすのが難しい
- アクセスパターンが異なるので、キャッシュもできない
Introducing Padlock: a global key-management system
暗号キーを管理するPadlockという内部のサービスを開発した。元となるキーを返すのではなく、派生したキーを返す。こうすることで、元となるキー自体はPadlockのみに留め、他のサービスに公開する必要がなくなる。
また、カテゴリごとに各ユーザのキーを別のものにしている。
Building at Spotify scale
- 直線的にスケールさせ、1ユニットあたり100万lookup/秒を実現させた
- 低レイテンシー
- 高可用性
- ダウンすると、個人情報を扱うサービスもダウンしてしまうので重要
- SLOでは99.95%に設定し、99.99%に上げられるかもしれない
Cassandraとmemcacheで構成。他のDBはまだテストできる状態でなかったので、移行するのもあり。
速くエラーレスポンスを返すために、リトライは行わない。その代わり、クライアントがリトライを行うか判断する。
Running Padlock with high reliability
高可用性を担保するために、いくつか工夫している。
- リリースフロー
- まずステージング環境で動作確認
- 承認を得た後に、3ステップ(1つのカナリーマシン、1つのリージョン、全グローバル)で本番環境
- プロビジョニング
- 全リージョンに配置することで、リージョン全体がダウンしても他のリージョンにリダイレクトさせる
- 2つのデプロイメントグループを用意した
- リスキーな差分のあるデプロイのリスクを軽減できる
- 片方のmemcacheがダウンしても、もう一つの方が稼働し続ける
また、フェイルオーバーを確実に行えるようにするため、人為的にサービスやDB、データセンター全体をダウンさせることで、機能するか確認を行っている。
Testing of Microservices
Why do we write tests?
テストが必要な理由は、以下。
- コードがすべきことをしている自信を与える
- 速く正確で信用でき、予測できるフィードバックを提供する
- テストを書くときに見落とされがちなメンテナンスを容易にする
Microservices test strategy
Integrationテストにフォーカスすべき。
Integrated Tests
Integrated Testsとは、「別のシステムの正確さに基づいて合格または不合格になるテスト」のことである。例えば、共有のテスト環境で実行しなければならなかったり、ローカル環境で他のサービスを立ち上げたりしなければならなかったりするとその兆候がある。
Integration Tests
DBやサービスを立ち上げ、APIコールをしてそのレスポンスを検証するようなテスト。実装の詳細は含まない。
実行に数msから数sかかるため、時間とのトレードオフだが、開発やメンテナンスのことを考えるとそれに見合う。また、実装が複雑な場合は、エラーになった際に実装の詳細を確認しなければならないかもしれない。
Final Thoughts
- コードがすべきことをしている自信を与える
- inputであるリクエストと、outputであるレスポンスを検証しているので、内部実装に関係なく結果がわかる
- 速く正確で信用でき、予測できるフィードバックを提供する
- フィードバックは十分速いが、テストが失敗した場合はスタックトレースを見ないといけないかもしれない
- テストを書くときに見落とされがちなメンテナンスを容易にする
- エッジからテストしているので、破壊していないと自信を持ってメンテナンスとボーイスカウトを行うことができる。本当に速く行うことができる
さらに、エッジからテストをしているのでサービスのIFのコントラクトを破っていないことを自信を深めることができる。
このようにマイクロサービスを分離されたコンポーネントとしてテストしているので、その意味ではマイクロサービスが新しいUnitとなり、それがマイクロサービスの実装の詳細のUnitテストを避けている理由である。
「Spotify――新しいコンテンツ王国の誕生」を読んだ
- 作者:スベン・カールソン,ヨーナス・レイヨンフーフブッド
- 発売日: 2020/06/18
- メディア: Kindle版
感想
Spotifyの創業前から、上場後の2019年まで幅広く網羅されていた。
全体的にはAppleとの対比がかなり記載されていて、古くのダウンロード形式のiTunesの時代から現在のApple Musicまで、Appleとバチバチやりあってた。
他には、Spotifyは他のサービスと違って、あくまでも生涯無料で使える点で異なることが強調されていた。
創業期では特に、CEO含め天才エンジニアが多く登場して、こういうのを天才と言うんだなと自分の無力さも感じたw
事業的には、音楽配信にレーベルと契約する必要があるため、ユーザが無料で利用できるようにするには創業時からかなりの資金が必要で、会長が前の会社のexitで大金を手にした状態からスタートし、スウェーデン、ヨーロッパ、アメリカ、世界と徐々に展開できた点も重要だったように感じた。
Amazonでのレビューがあまり良くなく翻訳が微妙と指摘されていたが、冗長な表現だと感じるところもあったが、そこまでは気にならなかった。
Spotifyが創業期からどのように成長して上場していったのかを知りたい人にはオススメ。
気になった箇所のメモ
はじめに
- Spotifyがここまで大きくなった最大の要因
- まだ他の誰もしていない無料サービスに賭けたこと。
- スウェーデンで起業して、僕たちのモデルをヨーロッパでまず展開して、その後段階を踏んで海外組織に成長させていったこと。おかげでこのモデルには将来性があると最終的に音楽業界に認めさせることができた。
- 2008年にリリース。
第1章 秘密のアイデア
- spotifyの名前は勘違いから生まれた。
- spot, identitifyの組み合わせの造語だと説明している
- BitTorentなどのP2Pモデルに興味を持っていた
- exitした資金のある起業家と創業
- コンサルタントとして、スウェーデンで技術力が一番になるぐらい会社を育てあげ、そこのエンジニアをCTOとして引き抜いた
第2章 リーダル通りのエンジニアが実現したとてつもない技術
- 無料でP2Pで配信するのはレコード会社にとっては無謀だったが、以下のような強みを持っていた
- exitした経験と資産
- プロダクトへの明確なビジョン
- 他のエンジニアを引きつけるCTOの魅力
- マイクロトレントを開発した天才エンジニアを採用
第6章 富裕層マネーが流れ込む
第7章 すべての音楽を無料で ── 爆発する人気とボブ・ディランのボイコット
- 70%支払いで、最低保障付きでスタート
- 1年目は赤字に
- 何年も振り込まれず、ボブディランたちがspotifyから脱退
- イギリスには2ヶ月後に参入
- 2週間で100万ユーザ獲得
- 毎日1万人増えるペース
第8章 フェイスブック経由でアメリカ進出
第9章 スティーブ・ジョブズ、いよいよ立ちはだかる
- アメリカの大手レーベルと交渉を進める
- 特にソニーには2.5%の株式を格安で購入するオプションを与えるなど、レーベルに有利な内容となった
- 2012年の夏にようやくアメリカ進出
- すでにストリーミングサービスはあったが、どれも有料会員限定のものだった
- 最初は無料会員は招待が必要だったので、話題になった
第10章 ザッカーバーグとの駆け引きと、密かに生まれた最大のライバル
第11章 規模を追い求めて ── ダニエルの本音と結婚
- appleにも対抗したり、レーベルにも大きな態度を取れるように、ユーザ獲得に集中した
- CEOはハードワークを求めるタイプ
- facebook経由で登録したユーザは、LTVが低かった
- アフィリエイトも試したが、赤字になった
- 新しい国でも展開し、facebookの利用率の低いドイツではメールアドレスで登録できるように
- 後ほど、世界でもfacebookアカウントなしで登録できるようにした
第12章 幻のプロジェクト「スポティファイTV」
- 数十人のリソースをかけて、動画部門を立ち上げハードウェアまで作ったが、音楽事業よりもお金がかかりそうだと気づき、断念した。
第13章 アップルとビーツ、ライバル2社が手を組んだ
- アメリカではなんの操作もせずに好きな曲が流れてくることが重要なため、ラジオやレコメンドの開発が急務となった。
- 46億ドルでアメリカのスタートアップを買収して、類似の曲を提供しようとしたが、既存の仕組みとも合わず大失敗に終わった。
- ビーツはストリーミングサービスをリリースしたが、トラブルだらけで技術的にも優れてなかった。永遠に無料のプランはない。
- Appleは過去最高額でビーツを買収。ストリーミングやヘッドホンが目的ではなく、ジミーアイオヴィンが持つ音楽業界での知名度を得ること。
第14章 スポティファイ史上最大の危機
- PCでのユーザ体験は良かったが、モバイル対応が不十分だったため、アクティブユーザ数が減少した
- モバイル対応をして克服
第15章 テイラー・スウィフトがボイコットし、ジェイ・Zが競合を立ち上げる
- 2014年、テイラー・スウィフトがspotifyがアーティストに十分に支払ってないとして、楽曲を取り下げた
- しかし、有料会員数への影響は限定的だった
- さらに、ジェイ・Zがスウェーデンの他のストリーミングサービスの会社を買収し、高音質かつ有料専用のサービスを立ち上げた。多くのアーティストが参画しており、音楽業界の構図が変わってしまうとspotifyにとって脅威となりうる存在。
第16章 ビッグデータでアップルミュージックに対抗せよ
- Apple Musicがリリース
- 最初の3ヶ月は無料利用できるが、その間にはアーティストには支払われないものだったが、リリース前にアーティストが反発して、支払うようになった
- リリース後も動作が遅く、半年間は不評だった
- Apple Musicのリリースに対抗するために差別化できる機能が急務となり2つの機能をリリースした
- 1つ目はmomentsで、そのときの気分に合わせて選曲するというもの
- 2つ目はweekly discoverなどの機能
- ユーザが作成したプレイリストからレコメンドを生成する
- CEOも注目していなかったが、spotify史上でも大きな成功となった
第17章 スウェーデンが生んだ「成功物語」として
- 無料会員の中でspotifyに時間を多く費やす客を識別し、彼らに割引キャンペーン(3か月は月額1ドルでプレミアム)を展開。
- また、急激に成長するために、数百万人のユーザ価格を下げ、有料会員数を増加させた。
- 2015年末には、有料会員の割合は、前年度の1/4から約1/3と成長した。
第18章 ストリーミング戦争をいかに生き延びるか
- アーティストから嫌われる存在で、新曲等はspotifyで提供されないことも多かった。
- アーティストに好感を得ようとジェイ・Zのダイダルを買収しようと打診したが、高すぎて白紙に
- サウンドクラウドの買収も試みたが、アーティストとのライセンスなどの契約内容が想像以上に悪く、成立直前で白紙に
- Appleからはアプリの審査のリジェクトを何度も受けたりして、妨害を受けていた
- Apple経由で契約した場合30%ほど値上げした料金体系を展開していたため、そのユーザに対して、直接契約するだけで割引になるとキャンペーンのメールを送った
第19章 いざウォールストリート ── 特殊な方法でIPOを果たす
- 上場前にテンセントと事業提携し、株式も互いに交換した
- テンセントも音楽事業で数億のユーザを抱えていたが、ほとんどが無料会員だった
- 2018年にニューヨーク証券取引所で上場
第20章 次なるステップへ
- レーベルから影響を受けにくくするために、アーティスト育成にも手を出し始めた
- 音楽のみならず、音に関する事業を制覇するために、podcastの会社や制作会社を買収した。
- 作者:スベン・カールソン,ヨーナス・レイヨンフーフブッド
- 発売日: 2020/06/18
- メディア: Kindle版
Product Manager Conference 2017【後篇】
だいぶ過去ですが、メモが見つかったので公開しておきます。
アジェンダ
- 今いるメンバーで「大金星」を挙げるチームの法則
- Product strategyによって組織は大きく成長できる
- 多様性を成功に導くプロダクトマネジメント 5選
- ユーザーの“心の声”を探るUXリサーチ
- PM が UXするために必要なのはおそらく IA
- プロダクトマネージャーの採用と育成
- 日本のプロダクトマネージャーは今何をすべきか
1. 今いるメンバーで「大金星」を挙げるチームの法則
登壇者:仲山 進也 氏(仲山考材(株)代表取締役・楽天(株)楽天大学学長)
グループとチーム
- グループをチーム化する(グループが成長してチームになる)
- チームづくりはジグソーパズルに似ている
- ピースを色とかでグルーピングして、組み合わせていく
- 人を職種とかでグルーピングして、組み合わせていく
- チームの成長法則
- 70点→赤点→120点
- 4つのステージ
- フォーミング(形成期)
- ストーミング(混乱期)
- ノーミング(規範期)
- トランスフォーミング(変態期)
- フォーミング(形成期)
- 人数が少ない方が簡単にうまくいく(多いと難しくなる)
- フォーミングの鍵:「心理的安全性」
- 失敗の秘訣
- 役割分担をしてから意見を言い合う(ストーミング風なことをする)
- アクティビティをやった趣旨は「ぷちストーミング超え体験」が味わえるから
- 共通言語を持てると、チームビルディングは加速します
- 無茶振りされてやりきった経験とか、クレーマーが最良の顧客になるのもストーミングを超える経験を経るから
2. Product strategyによって組織は大きく成長できる
登壇者:詫間 亮平 氏(楽天株式会社 レジャーサービス開発課 ゴルフプロダクトマネジメントグループ マネージャー)
- 担当事業
- 楽天GORA
- サービスできて13年で、国内60%シェア
- 組織構成
- buisiness
- Sales
- UI/UX
- Marketing
- developmet
- PM
- Engineer
- QA
- buisiness
Product strategyによって組織は大きく成長できる
- 改善前の問題点
- 頻繁な優先順位変更
- 競合リリースの機能差分
- ユーザからの問い合わせ
- 皆が目前の作業没頭
- 多方面に案件が膨れ上がる
- 数打てば当たるみたいな考え
- リリースしても利用されない
- 開発してもリリースされない
- メンバーからの不満
- 方向性、提案しない、やらされている感
- 頻繁な優先順位変更
- 改善後
- Product
- 勇気を出して一歩引いて、広い視野で先を見る
- ビジョンを浸透させる
-課題の特定
- 分析
- 何故No1になれたのかの分析
- 1990年から縮小傾向の業界
- その縮小の本質的な原因を特定する
- ゴルファーをつなげるコミュニティに
- 方向性
- 国内:新規獲得からヘビーユーザを囲い込むように変更
- 海外:ブルーオーシャンで、アウトバウンド・インバウンドに力入れる - 組織体制
- 分析
- PMとエンジニアを別グループにした
- 以前の問題点
- PMが工数とかを考慮してしまった
- 本来やるべきことを実行できない
- エンジニアの成長機会を奪う
- 改善後
- PMはプロダクトを全身させるコトだけを考える
- エンジニアが、システムを巻き取る
- PMがエンジニアに業界背景や問題、解決策を与える
- エンジニアがつくりたくなる熱狂的なものに
- プロフェッショナルな関係になる
- KPIを常に表示して、エンジニアが提案するようになった
- 戦略ありきで進める
- Product
- その他
- 仕事の半分は考える時間に
- whyを突き詰め問題の本質に
- StrategyでInovationを起こせない
- やらないことを決めるところが腕の見せどころ
- 理想を描いて、皆が熱狂するstrategyを描く
- 何度も繰り返し提案する熱意情熱
- 浸透するには数年かかる
- 最初は浮く存在になってしまう、批判は覚悟する
- 結果が出れば批判は消える
3. 多様性を成功に導くプロダクトマネジメント 5選
働き方の多様性 | 大阪リモートチームとのプロダクトマネジメント
登壇者: 尾部 絵里子 氏(Sansan株式会社)
- 未来のことを考えることが最重要
- PMはワイヤーフレームを書かない
- そうするのは自走できるような環境にすることが重要
- リモートとのコミュニケーションコストを下げる目的でも
4. ユーザーの“心の声”を探るUXリサーチ
登壇者:奥泉 直子 氏(フリーランス・ユーザーリサーチャー)
アジェンダ
- UXリサーチとは
- UXサーチ方法
- ヒトの認知特性
UXリサーチとは
- UXとは
- ISOでは製品やサービスを使用したとき、また使用を予測したときに生じる個人の知覚や反応
- 環境文脈
- 時間軸
- 人の内面
UXリサーチとは
- Research Toolbox
- UCD
- インタビュー(人の内面)
- 日記調査(時間軸)
- エスノグラフィ行動観察(環境、文脈)
- これらの調査を組み合わせる(仮説と検証をはっきりさせるため)
ヒトの認知特性
まとめ
- UXのリサーチする
- まず、ユーザインタビュー
- 人の認知特性を知る
5. PM が UXするために必要なのはおそらく IA
登壇者:小久保 浩大郎 氏(株式会社CAMPFIRE)
- 参考記事
- IA
- スキルセット
アジェンダ
UX is 何?
- Userとプロダクトを取り巻く全部のことを指す
- UXは環境やコンテキストに依存する
- ユーザと対象の二者間のものではない
- それらを取り巻く閑居やコンテキスト
- UXは発明されたのではなく、発見された概念である
ユーザビリティとUX
マーケティングとUX
- 対象ユーザのペルソナをどう考えるか
- ユーザの分類
- innovator
- early adopter
- early majority
- late majority
- Laggard
- 学習曲線のデザインが変わる
- サービスの提供価値などの理解度
- よく使う機能を意識せずに使える
- 高度な機能を見つけて使いこなす
UIとUX
- PMが考えないといけないモデルたち
- ビジネスモデル
- システムモデル(事実)
- メンタルモデル(認知)
- (ここではプロダクト= システムモデル + メンタルモデル)
- 事実と認知をどうするか
- システムモデルとメンタルモデル
- 単純に一致させればいいものでない
- 効率的で合理的なシステムの動作モデルと、ユーザがわかりやすく使いやすいモデルは違うことが多い
- どちらが先に決定する訳ではなく、相互に影響しながらできあげることも多い
- 恐らく昔はシステムモデル先行だったが、ユーザビリティや人間中心設計といった概念の普及により改善された
- UXという概念の普及もこの流れの一貫といえる
- メンタルモデルは2つに分類
- デザイナーモデル(こう思わせたい)
- ユーザモデル(こう思った)
- このブリッジになるのふぁUI
- デザイナーモデルとユーザスモデル
- 可能な限り一致させたい
- それをどう上手くやるのかというのがUIデザイナー
- UIを通して
- ターゲットペルソナは複数いる
- プロダクトサイクルによって最適が変わる
名前重要
人はそもそも物事をどのように理解するか
- 理解 = わかる = 分かつ
- 名前を与えられて始めて区別できる
- 別々の名前をつけることで2つの存在
- 分かつことによって、その抽象的性質を帰納的に推論できるようになる
- この抽象モデルが「理解」
基本的なところは統一した方が良い
- システムの根幹的な概念や動作モデルに関する名称
- 主要なオブジェクトたち(名詞)
- それらが取りうる振る舞い(動詞)
- 利用シーンにおけるユーザの区別(名詞)
- ユーザが取りうるアクション(動詞)
統一する利点
6. プロダクトマネージャーの採用と育成
GoogleMapのPM
- 3種の境界付近
- UXデザイン
- エンジニアリング
- ビジネス
- 関係構築も仕事の一つ
- リーダーであることが求められる
日本でのGoogleのPM
- マウンテンビューと日本で仕事そのものの大きな違いはない
- 違うオフィスに行くことが重要
- 早朝にMTGなりがち
- 鉄道システムとか最先端
APM
- Associate Product Manager
- PMの赤ちゃん
- 2年間で2プロジェクト担当する
- PMが足りない中で、情報専攻学生を育てるでMerrisaMayerがAPMを設立
- 何故特別か?
- いろんな文化を体験することができる
- APM同期が財産になる
- いろんなプロダクトのPM担当と繋がりを持てる
APMトリップ
- 4カ国回って国の違いを
- 文化の違いを学ぶ
どうやってAPMになるか
- 面接
- 技術の質問
- 適性、やる気、クリエイティブ重視
- 経験必要なし
- APMを卒業してPMになる
7. 日本のプロダクトマネージャーは今何をすべきか
UberでのKafkaのスケーリング
私もKafkaを使っているが、このレベルのデータ量やトピック数、パーティション数を扱ったり、パフォーマンスを求められてる訳ではないため、難易度が格段に上がる印象を受けた。
パフォーマンスを優先させながらも、データロスを抑えたり、検知する仕組みを導入して解決したのが面白かった。
また、複数のデータセンター間でのKafkaメッセージの集計を漏れなく行い続けるだけでも、トピック数やデータ量が増え続ける環境下では難しく、オープンソースを開発して解決したのも海外の大きいテック企業らしいと感じた。
How Uber scaled its Real Time Infrastructure to Trillion events per day
Use cases & Current Scale
ほとんどリアルタイムで処理が行われている。
- ドライバーとのマッチング、到着時間の計算
- UberEatsの料金計算
- 不正検知
- ドライバー、乗客の新規登録など
Kafkaのユースケース
- pub/sub
- ストリーム処理
- 分析
- データベースのchangelogの転送
- ログ
How We Scaled The Infrastructure
stats: 1兆メッセージ/day, PBsレベルのデータ量、数万のトピック
要件
- 水平スケーリング
- APIレイテンシーは5ms以下
- 99.99%の可用性
- 99.99%の永続性(顧客に致命的なものは100%)
- マルチDCのレプリケーション
- Java, Go, Python, Node.js, C++に対応
- Auditing
パイプライン
高速化させるために、各ステージで非同期でバッチ処理するようにしている。そうすることで、数ms以内にレスポンスを返すことができる。
Rest Proxy & Clients
なぜ、Rest Proxyを挟むか?
- クライアントのAPIを単純化して、複数の言語に対応するため
- Kafka brokerとクライアントを疎結合にするため
- 薄いクライアントだと、オペレーションが簡単に
- Kafkaのバージョンアップを行いやすく
- 信頼性を向上するため
内部
- オープンソースのRest Proxyがベース
- パフォーマンスの改善
- トピックのメタデータのキャッシュ
- 信頼性の向上
- Fallbackクラスターのサポート
- 複数producerのサポート
- コミュニティに還元する予定
Local Agent
producerライブラリ(アプリケーションのProxyクライアント)
Local Agent
uReplicator (MirrorMaker)
Kafkaのデータを高速かつ信頼性高く、複製するためのオープンソース。KafkaのMirrorMakerをベースに信頼性を高め、データロスが0になるよう保証させるために拡張させた。
MirrorMakerとは
Uberでは複数のデータセンターのKafkaクラスターを持っているが、ビジネスロジックや分析のために1つのデータセンターにデータをまとめないといけない。データセンター間のレプリケーションにMirrorMakerを使っていた。仕組みとしてはシンプルで、対象のクラスターからデータを取得するようにconsumeして、それを集計先にプロデュースする。
UberではMirrorMakerを導入したが、徐々にスケーリングの問題が発生して、遅延やデータロスが発生した。主な原因は以下の通り。
- リバランスのコストが高い
- リバランスすると処理が止まってしまい、メッセージが蓄積してリバランス終了後に大量のデータが流れて、consumer側に問題が発生した。
- トピック追加が難しい
- MirrorMakerの対象に含めるかどうかをトピックのホワイトリストで管理しなければならないため、変更の度にリスタートが必要。
- データロスが発生: (過去のバージョンのバグ)
- メタデータの同期
どのように解決したか
- 複数のMirrorMakerクラスターに分割
- レプリケーションにApache Samzaを使用
- Apache HelixベースのKafka consumerを使用
uReplicatorの概観
4つのコンポーネントから成る。
- Helix uReplicator controller
- uReplicator worker
- Helix agent
- topicの変更の通知を受け取り、DynamicKafkaConsumerにトピックとパーティの操作を指示
- DynamicKafkaConsumer
- 高レベルのconsumerの修正で、リバランスをなくし、オンラインでトピックとパーティションの操作を行う仕組みを追加する役割
Reliable Messaging & Tooling
At-Least-Once
高スループットをサポートするために、永続化前にackedしているので、ノードが永続化する前にダウンするとデータロストしてしまう。ログなどの情報はそれでも許容できるが、支払いなどの絶対データロストできないものには不十分。
支払いなどは、正規のKafka brokerがackを返すまで待ち、永続化させる。その代わりレイテンシーは大きくなってしまう。
Chaperone (Auditing)
各ステージで10秒のウィンドウ時間ごとに、Chaproneという内製ツールにauditを保存し、ステージ毎の差分を見て、データロストが発生していないかを監視する。
概要
4つのコンポーネントから成る。
- AuditLibrary
- auditのアルゴリズム、定期的に10分のwindowごとに集計して結果をメッセージとして出力する。
- Kafka Rest ProxyやKafka brokerにメッセージを渡す。
- ChaperoneService
- Kafkaからメッセージをconsumeして、auditのためにタイムスタンプを保存する
- 定期的に、あるKafkaのトピックにauditメッセージを生成する
- ChaperoneCollector
- ChaperoneServiceの生成したauditメッセージを取得してDBに保存する
- そのDBからダッシュボードを表示して、遅延やデータロスが発生していないか確認できる
- WebService
- webフロントエンドで、UIから簡単にメトリクスを確認できる
設計要件
- 各メッセージを漏れなく重複なく(exactly once)数える
- メッセージにUUIDを付与し、Kafkaに送信する前にWALに永続化させる。Kafkaからackが返ってきたらdoneのステータスに更新する。
- クラッシュした場合は、そのステータスを見て再送する。
- ティアにまたがったメッセージを監査するために一貫したタイムスタンプを使う
- JSONにタイムスタンプが含められるが、decodeしているとパフォーマンス上問題が出るので、独自のパーサーを開発した
ユースケース
- データロスの検出
- この開発までは、メッセージのconsumerがデータロスに気づいてからしか判明できず、特定や原因の調査も難しかった
- Kafkaで使用可能なオフセットを超えてデータを読み取る
- Kafkaは過去のデータの取得に制限があるが、同じインターフェースで過去のデータも取得できるようにした
- こうすることで、開発者は特定の時間の問題をデバッグしたり、必要あればメッセージをリプレイできる
Cluster Balancing
- 自動のリバランスがサポートされていない
- 手動の配置は難しい
Uberのリアルタイムマーケットプラットフォームのスケーリング
Scaling Uber's Real-time Market Platformという2015年のUserによるスケーリングに関する講演を見たので、簡単にまとめた。
他の海外の大きなサービスと比べても、技術的にはかなり複雑なように感じた。ユーザとドライバーをマッチングさせるところだけでも、リアルタイムで位置情報を更新するように大量のwriteリクエストを捌く必要がある。また、その位置情報からどのドライバーが最適なのかを選ぶのも、車種や乗車可能な人数、距離の計算なども遅延なく行わなければならない。また、支払いや
技術的に面白かったのは、スケーラブルと可用性を達成するために、スレッドがkillされたりノードがダウンするのも正常系の一部とする前提で設計が行われていたことだった。位置情報からの高速な検索も全く触れたことがなかったので興味深かった。
アーキテクチャ
- dispatch: 乗客とドライバーのマッチングとか。
- maps/ETA: 交通マップや到着時間の計算など。
- database: 色んな種類のものが使われている(Postgre, Redis, MySQL, Riak)
- Post trip pipeline: 到着後の処理を扱う(ドライバー評価、メール送信、支払いなど)
- money: 外部の支払いサービスとの連携
Problems
旧システムでは、以下のような前提で設計されていて、スケールさせるのが難しかった。アンチパターンではあるが、Dispatchを全部書き直した。
- 1ライダーに対して1台: Uber Poolとか他のビジネスに対応するのが難しい
- 人が動く: UserEatsのように食品とかが動くように適応するのが難しい
- 都市ごとにシャーディング: さらに都市を追加してスケールするためには、都市の大小差や負荷の差を考慮しなければならない
- MPOF: Many Points Of Failures
Dispatch
supply(ドライバー)とdemand(乗客)をマッチングさせるサービス群。主にNode.jsで作られている。
位置情報は4秒に1回更新されるので、100万RPSに耐えられるぐらいかなりスケーラブルである必要がある。
主に以下のようなサービスから成る。
- supply: ドライバーの情報(何人まで乗れるか、チャイルドシートはあるかなど)
- demand: 注文の情報(何人乗る必要があるかなど)
- DISCO: ビジネスロジックなど
- geo by supply: ドライバーの位置情報
- routing/ETA(Estimate Time of Arrival): 道路情報を加味して、距離を計算したりする
- geo by demand: 乗客の位置情報(例えば、空港の何階にいるかなど)
S2
地球は球体なので、経緯度だけでは正確に計算を行うことが難しいため、S2を導入した。S2はGoogleのライブラリで、位置情報や計算を簡単に行える。
球体表面をcellで分割し、cellはidとlevelで表現され最小のcellは0.48cm2で、これは64bitで表現できる。
Level | 最小面積 | 最大面積 |
---|---|---|
0 | 85,011,012 km2 | 85,011,012 km2 |
1 | 21,252,753 km2 | 21,252,753 km2 |
12 | 3.31 km2 | 6.38 km2 |
30 | 0.48 cm2 | 0.93 cm2 |
このidをシャーディングとインデックスのキーとして使うことで、該当エリアにいるsupply一覧の取得等ができる。
Routing
目標
- 待ち時間を減らす
- 余分な運転を減らす
- 全ETAで最小にする
ベストな選択は、現在乗車可能なものとは限らない。例えば、今から1分後に近くで空きになるタクシーがベストな選択かもしれない。
スケーリング
Node.jsはそれぞれのモバイルのクライアントとのstateを保持しているため、単純なアプリケーションの水平スケーリングは適用できない。DBなどと同様にアプリケーションをstatefulにスケールさせないといけない。そこで、ringpopを開発した。CAP定理ではAP型で、可用性を重視している。ゴシップ型のメンバー管理で、アプリケーションレイヤーのシャーディングを行っている。クライアントからリクエストが来ると、適切なノードにリダイレクトする。
SWIM(Scalable Weakly-consistent Infection-style Process Group Membership)プロトコルがベースになっている。
TChannelというプロトコルでノード間の通信を行う。以下のような目標をもって設計されたプロトコル。
可用性
ダウンしていると、他のサービスに流れてしまうので、遅延実行できるようにするなどしておかないとビジネス的に重要。
- すべてリトライ可能に
- すべてkillできるようにする: 失敗やダウンは例外ではなく、正常系の一つとして扱えるように。
- クラッシュのみに対応: グレースフルシャットダウンに対応すると複雑性も増し、ちゃんと機能するかの確認も必要になってしまう
- 小さく分解する: 大きいインスタンスが少数だと、killするリスクが大きくなる
カルチャーチェンジ
- すべて(DBでさえも)killする: Redisはリスタートのコストが高いので、この観点ではよくない
backup requests with cross server cancellation
分散システムで、レイテンシーを減らす方法。例えば、平均のレイテンシーは良いが、99パーセンタイルのレイテンシーが悪いときに、同じ種類のサービスに2リクエストを投げれば、どちらかのレスポンスは速く返ってくる可能性が高い。
詳細には、少し遅延してから2度目のリクエストを投げ、また1つ目のサービスは2つ目のサービスにキャンセルリクエストを投げる。遅延があることで、通常は1つ目のサービスのみが処理して、タイムアウトなどで失敗した場合は、2つ目のサービスがより低いレイテンシーで処理を返す。
更に詳細は、こちら。
データセンターのダウン
DCが切り替わってもセッションや処理が継続しているかのように見せるために、定期的に状態のダイジェストをクライアントに保持させておき、データセンターがダウンすると、その情報をもとに再開させる。