2016年4月27日
ソフトウェアのスケーラビリティについてスターバックスが教えてくれること
本記事は、原著者の 許諾のもとに翻訳・掲載しております。
2004年に Gregor Hohphe が「 スターバックスでは2相コミットを使わない(Starbucks Does Not Use Two-Phase Commit) 」という優れた投稿を発表しました。それを読んでいたら、学生時代にスターバックスでアルバイトをした頃がいきなり関わってきました。何年もの間に次第に分かってきたのは、プログラマでさえ有名なコーヒーショップのチェーンから学べることが思った以上にあるということです。
多くの人はスケーラビリティのあるソフトウェアを作ろうしますが、最初に考えていたよりも非常に難しいことがあります。個々のタスクをこなしているうちに「あらゆるものの重要性は等しく、同じリソースを必要とし、決まった順序で同期的に進行する」と考えてしまう罠に陥ってしまうのです。
実際には、少なくともスケーラビリティのあるシステムでは、当てはまりません。もちろんスターバックスでもそうです。
コーヒーを出す手順
スターバックスでは4つの段階を経てコーヒーが出されます。最初に、早い者勝ちのルールにしたがって、客がカウンターの前で行列(キュー)を作り、オーダーします。次に、店員(バリスタ)が客からオーダーを受け、代金を受け取ります。第3段階では、店員が飲み物を準備します。第4段階では、用意ができたところで飲み物をカウンターの上に置き、客の名前を呼びます。
訳:オーダーを受ける→代金を受け取る→コーヒーを作る→コーヒーを渡す
これは理にかなったモデルのようですが、あっという間に長い列ができてしまいます。1人の人間がいちどきに1つ以上のことを行うことはできません。結局、バリスタが順番に1つずつオーダーをこなしているうちに客は列を作り始めます。もし、もっと多くの客に対応しようとするなら、スケーラビリティを拡張しなければなりません。それにはどのような方法があるか見てみましょう。
バリスタのスケーラビリティの拡張
スターバックスがスケーラビリティを拡張する方法の1つは スーパーバリスタ 、つまり才能があって、仕事を手早くこなせる、聡明な人物を雇うことです。スーパーバリスタは自らの成長を大いに心がけ、仕事のあらゆる局面を最適化し、常に効率性を上げなければなりません。ソフトウェアの世界ではこのようなアプローチを スケールアップ(垂直スケール) と言います。
スケールアップ 戦略には、「1人が作業できる早さ(と時間の長さ)には限界がある」という問題点があります。スーパーバリスタといえども、ある時点で要求に応じられなくなってしまいます。そうなった場合には、客がフラストレーションを感じて店を離れ、戻って来なくなるかもしれません。
同じように、全てがシーケンシャルに進むソフトウェアでは、最適化できる範囲に限界があります。200GHzのCPUは買えません。最大のCPU群でさえ、最高でもクロック周波数が3~4GHz以下のコアによるマルチコアです。
スターバックスでスケールする方法として、 普通の 店員を増員できる業務体系にするという手もあります。これはソフトウェアで言うところの 並行処理 そのものです。具体的には、1人のバリスタがオーダーを受けると、もう1人のバリスタがそれを作り始めます。すると最初のオーダーの準備中に、1人目のバリスタは並行して次のオーダーを受けることができます。
需要があるレベルに達した時のみ、並行処理を考慮に入れるのがベストだと考える方もいるでしょう。でも残念ながら、そう簡単にはいきません。必要な時だけ並行処理に切り替えられるような魔法のスイッチはありません。そのため、あらかじめ準備が必要です。
スターバックスはこれを承知で、たとえ店員が1人しかシフトに入っていなくても、並行処理に必要なものは新店舗の開店初日から全てそろえています。つまり、いつでも増員できる体制を整えているのです。
教訓:円滑に並行処理を導入するには、並行処理に対応したシステムの構築が必要である。
これをスターバックスが、どのように実現しているか見ていきましょう。
メッセージの伝達が基本
スターバックスでコーヒーを頼んだ経験がある方なら、カップの四角いマークのところに記号が書かれているのを見たことがあるでしょう。この記号はバリスタが使う略語で、飲み物やトッピング(ホイップクリーム、フォームミルクなど)を一目で特定するために使われています。
カップ、あるいは メッセージ は、店員同士でコミュニケーションを取るために欠かせないものです。カップはバリスタに飲み物を用意する必要があることを知らせる合図になり、そこに書かれた記号を見れば用意する飲み物の種類が詳細に分かります。店が混雑しておらず、カウンターで接客中の店員が1人しかいない時でも、店員は必ずカップに記号を書きます。
一見すると、これは無駄な作業にも思えます。でも、この作業をしていれば急にたくさんの客が入店した時、待機していた店員はすぐにヘルプに入れます。彼らは、もろもろの伝達事項を聞かなくても、メッセージ通りに飲み物を作り始めることができるのです。
教訓:店員を増員して作業を分担できる状態が常に整っていれば、急に忙しくなっても問題は生じない。そのための一手段として、メッセージの利用がある。
分割統治
上述のように、コーヒーを作る一連の作業は店員、つまりバリスタ1人で担当できます。しかしスターバックスのデフォルトの人員配置では、1人の店員(レジ担当)がオーダーと会計を処理し、別の店員(バリスタ)が飲み物を作るようになっています。
通常は、コーヒーを作る工程に一番時間がかかるので、店が混んでくると複数人のバリスタが飲み物を準備します。彼らは大抵、同じところからカップを取って、仕事を平等に分担します。これは Competing Consumers(競合する利用者) というパターンの一例です。
しかし、この方法で問題が生じるケースもあります。例えばバリスタが3人いて、コーヒーのマシンとフラペチーノのマシンがそれぞれ1台ずつある場合を考えましょう。この状況で3人の客がコーヒーを、その次の客がフラペチーノを頼んだとします。オーダーを受ける担当が、それぞれ該当する記号を書いたカップを4つ順番に並べると、バリスタは各自コーヒーを作るためにカップを手に取ります。この場合、1人目がコーヒーを作り始めると、他の2人はコーヒーマシンが空くのを待つしかありません。
このようなリソースの競合は、作業を分割することで回避できます。その方法の1つが、メッセージをさらに細かい種別に分割して、別々に処理できるようにすることです。例えば先ほど説明したように、スターバックスではカップを用いて飲み物の準備が必要だと伝えています。このシステムでは同時に、温かい飲み物と冷たい飲み物を区別しています。温かい飲み物は紙のカップで、冷たい飲み物はプラスチックのカップで出します。つまり温かいコーヒー3つ、その次にフラペチーノ1つという順番でオーダーが入ると、そこには紙のカップ3つと、プラスチックのカップ1つが別々に分けて置かれた状態になります。1人目のバリスタは、最初に置かれた紙カップの中からカップを1つ取って飲み物を作り始めます。2人目のバリスタはコーヒーマシンが使用中なのを見ると、次に置かれたプラスチックカップの中からカップを取り、代わりにフラペチーノのマシンを使います。こうすれば、両方の飲み物を並行して用意できます。
こうしてバリスタがタスクを分担して並行処理をするような作業分担を、 パーティショニング と呼びます。
教訓:パーティショニングはスケーリング戦略において非常に重要な要素だと言える。全ての作業を同じ基準でスケーリングする必要はなく、すぐに終わる小規模なタスクは1人で、手間や時間のかかる作業は複数人で対応するようにする。このようにパーティショニングを行えば、それぞれのアクティビティを個別にスケーリングできる。
作業ごとに重要性は異なる
スターバックスの成功要因の1つは、常連客を把握することの重要性を、トレーニングを通じて店員に認識させている点です。常連客とは、例えば毎朝チームのためにベンティサイズのアメリカーノとグランデサイズのラテを2つずつ買いに来る男性や、毎週水曜日にトールサイズのキャラメルマキアートを頼んで、店内で1時間ほど読書する女性などです。
水曜日に、”トールサイズのキャラメルマキアートの女性”が店に入ってきたことにバリスタが気付いたとします。店員は、彼女がカウンターにたどり着く前に女性のお気に入りの飲み物を準備し始めます。欲しい商品を伝えなくても分かってくれたことに、この女性客はうれしい驚きを感じます。レジの担当者は彼女がいつも頼む飲み物をあらかじめ知っていたので、彼女にあいさつをして代金を受け取っただけでした。支払いが済む前に、彼女のコーヒーは既にカウンターの上に用意されているのです。
スターバックスの常連客の割合がいかに高いかを知ったら、皆さんはきっと驚くでしょう。常連客にできる限り素晴らしい体験をしてもらうことに、高い優先度が置かれています。常連客は、他の客よりも早く飲み物を受け取ることがよくあります。このことによって、常連客は自分が大切にされていると感じ、また来店したいと思います。このようにして、スターバックスに対する彼らの価値観は高まっていくのです。
教訓:他に比べて重要度の高いタスクがある。標準的な活動を、再利用可能な個々の基本的な要素へまとめれば、必要に応じて簡単にプロセスを修正でき、より価値の高いタスクへ優れたサービスが提供される。
全てのミスを防ぐ必要はない
これまで紹介した例ではいずれの場合も、スターバックスの店員は、客がコーヒーを受け取る前に支払いを済ませたことを確認する必要があります。そのために、バリスタは客に飲み物を手渡す際にレシートを提示するようにお願いすることが 可能です 。しかし実際にはそういうことは行われません。
代金を払わずにコーヒーを受け取ろうとする人はほとんどいないということに、スターバックスは気が付きました。彼らの分析によると、時々起きるコーヒーの紛失を防ごうと注意するよりも、注文への対応に専念する方が利益になるのです。もし誰かがたまたま、他の人が注文したコーヒーを受け取ってしまったら(これは通常、手違いが発生した場合にのみ起こることです)、バリスタはその人のために無条件で新しいコーヒーを用意するでしょう。
教訓:スケーラブルなシステムを構築するには、ある程度のミスは避けられないという考え方を受け入れる必要がある。ミスを完全に防ごうとすると、コストがかかり過ぎる。そのかわりに、ミスが発生したら早急に問題を見つけて、確実に補うことに注力するべきである。
まとめ
一見、単純に見えるコーヒーを作る4段階の作業が、興味深いビジネスのプロセスへと発展しました。ちょっと見には 例外的 でレアケースだと思われたことが、ビジネスの本質的な部分であると分かりました。
需要の急増やミスは、一日に何度も起き得る事態です。こうした事態にうまく処理できるシステムをデザインするには、一般的な想定に疑問を投げかけてみることが必要です。多くの場合、最初に思いついたモデルは、そうした課題への配慮が欠けています。また、考慮すべき 例外的 な状況はたくさんあります。例えば、注文のキャンセルは それ自体がとても興味深い問題です 。
スターバックスの例で分かるように、ビジネスにおいて単純な取り組みをしていたのでは、より多くの利用客にサービスを拡大することはできません。客が増えれば増えるほどサービスのレベルは低下し、客足が遠のくことになってしまうでしょう。そうならないよう、増大する要求に対応できるように仕事を体系化する必要があります。結局、スケーラビリティのあるシステムを構築することは、テクノロジについて考え直すと同じように、ビジネスのプロセスを考え直すことなのです。
スケ―ラビリティのあるソフトウェアの構築方法についてもっと知りたい方は、以下の参考資料をご覧ください。
- 非同期メッセージによるスケール
- ファストフードの実例から得られる Sagaのパターン
- 皿洗いとchain of responsibility (責任の連鎖)
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa