趣味はオープンソースのプロジェクトを作ること

Taro L. Saito氏:「Scala For Everything」というタイトルで発表します。簡単な自己紹介を。Treasure DataのSaitoといいます。Principal Software Engineerをしていて、今、カリフォルニア、アメリカの西海岸から発表しています。タイムゾーンの都合上、今こちらは夜の20時なんですけれども、ScalaMatsuriの運営の麻植さんが、タイムゾーンに配慮してくださったので、非常に感謝しております。

もうアメリカには5年ぐらい住んでいます。バックグラウンドとしては、コンピュータサイエンス、特にデータベースシステムやゲノムサイエンスなどデータサイエンス寄りのこともやっていました。

趣味としては、オープンソースのプロジェクトを作ることが好きです。今日は、このAirframeの話を主にするんですけれども、それ以外にも、例えばsnappy-javaなど、Parquetのデータフォーマットだったり、Sparkなどに使われているものを作っていたりします。

あとはScala関連だと、sbt-sonatype。これはScalaのプロジェクトをMaven Centralにパブリッシュするためのプラグインです。数千以上のScalaプロジェクトで使われているsbt-packプラグイン、ScalaのプロジェクトをDockerイメージのコンテナにするものなど、そういうのをサポートするためのパッケージプラグインなども公開しています。

また最近、「データ指向アプリケーションデザイン」という書籍の翻訳をお手伝いしました。これは非常によい本です。10年ぐらいこういう本がなかったんですけれども、ついにこういう本が出てきて、分散データシステムの入門の決定版というべき、非常にオススメできる本です。ぜひ手に取っていただけると。600ページぐらいある分厚い本なのでけっこう読むのが大変なんですけど、非常にためになる本です。

今日のトピック

今日のトピックです。今日はいろいろなテクノロジーについて話します。特にScala、Scala.jsを中心に、サーバーサイドとフロントエンドサイドの技術についていろいろ触れていきますけれども、いろいろ学ぶべきことがたくさんあるんですね。ただ、それを学ぶコストが非常に大変高い。そこでAirframeというライブラリを使って、このいろいろな技術に関してすべて忘れられるようにする。そういう話を一応今日はしていきたいと思います。もうすべてScalaでなぎ倒す。

Scala.jsは基本的にScalaと一緒のシンタックスで書けるので、Scala.js自体も実は忘れることができるという話をしていきます。そして残ったのがAirframe。Airframeはオープンソースのライブラリで、Scala中心のサーバークライアント開発ができるように配慮されたものです。

最終的な目標としては、Airframe自体も忘れて、Scalaのことだけを考えてフロントエンド・バックエンドの開発ができればいい。そういうところを目標にして今日はお話をしていきたいと思います。

Airframeとは

そもそもAirframeは何かというと、Treasure Dataで使われているScala関連のサービスのコアのモジュールです。オープンソースとして共通ユーティリティを外に出していて、今は20を超えるライブラリが含まれています。例えばロギングだったり、Dependency Injection、あるいはJSON/MessagePackパーサーなど、Scalaでプログラミングをする上で有用なライブラリがこのAirframeの中に含まれています。

Airframeの概要については、2019年の「ScalaMatsuri 2019」で一部紹介しました。今回、2020年のScalaMatsuriではAirframeのRPCやRx、Reactive DOM Renderingなど、そういう話をしていきたいと思います。

もう1つ、AirSpecという新しいtesting libraryもAirframeの中に同梱されています。本当は2020年の「Scala Days」で発表する予定だったんですけれども、残念ながらコロナの影響でScala Days自体が2020年はキャンセルになってしまったので、もしかしたら来年シアトルで開かれるScala Daysで、この話ができると思っています。

Treasure Dataについて

簡単にTreasure Dataが何をやっているかについて紹介します。Treasure Dataはすぐに使えるクラウド上のデータプラットフォームを提供していて、クラウド上にデータを集めるために弊社のオープンソース製品であるFluentdとEmbulkなど、これらのオープンソースのツールを提供しています。

これらのツールを使って集めたデータをTreasure Data本体のほうに保存していく。その保存されたデータに対して、例えば分散クエリエンジンであるPresto、Hive、Sparkなどを使って日々データを解析できます。毎日毎日新しいデータを処理し続けるので、たくさんのジョブを管理するためのワークフローツールDigdagなども動いています。

Treasure Data自体はMessagePackベースのカラムナーストレージ(columnar storage)をデータベースとして使っていて、わりとユニークなデータベースサービスになっています。

Prestoについて

僕の担当しているチームでは、主にPrestoの運用をしています。このPresto、実行されるクエリ数が1日あたり100万を超えていて、これはPrestoユーザーの中で世界一の規模のPrestoのusageです。クエリの数が100万を超えているだけでなく、Prestoクラスタが世界各地4か所以上に分散されているので、監視するのが非常に大変になってきました。そこでScala.jsを使ってPrestoのクエリを監視するモニタUIを作っています。

この画面はPrestoの「Conductor」という内部ツールです。リアルタイムに動いているクエリの状況を確認できますし、historical――歴史的に――、過去に実行されたクエリの実行時間とかそういうものも同時に表示することで、現在動いているクエリがどれぐらい性能要件を満たしているかを確認することができる。これ全部Scala.jsで書いていて、リアルタイムに動くものになっています。

さらに別の例として、SparkをサポートするためにAirframeとScalaでいろいろなサービスを作っています。

Treasure Dataは「MessagePack Columnar Format(MPC1)」という特殊なデータフォーマットを使っています。これをSparkから読み書きするためのデータアクセスAPI「Plazma Public API」というものを、Scala、FinagleベースのWebアプリケーションサーバーを作って運用しています。

中身ではAirframeのモジュールが非常に多く使われていて、サーバーサイドだけではなく、Sparkの機能を拡張するために、いろいろAirframeのDIだったりAirframeのMessagePack Readerだったり、そういうモジュールを組み合わせてSparkの機能を拡張して、Treasure DataのMPC1ファイルを読み書きするための機能を提供しています。

この話のより詳細な話は「Spark Meetup Tokyo」という2020年開かれたミートアップでのスライドにも紹介されているので、ぜひそちらもご参照いただければと思います。

Scalaの応用範囲

この2つの例で見たように、Scalaというのは、UIを作ったりバックエンドサーバーを作ったりと、非常に応用範囲が広いんですね。特にバックエンドのほうはJVMのエコシステムを利用できるので非常に強力です。

例えばnettyという高速なネットワークアクセスを可能にするライブラリがあります。Scala界隈で有名なAkkaとFinagleなどのWebフレームワークはすべてnettyベースで実装されていますし、GoogleのRPCサービスの実装であるgRPC、例えばgRPC-Javaではnettyが使われています。なので、Javaの資産が使えるというところで、高速なWebサーバーを作るのは非常に簡単になっています。

一方、フロントエンド。2020年はすごくよい年で、Scala.jsのversion1.0.0がリリースされました。現在、最新のバージョンはマイナーアップデートを経て1.3.0になっていますが、この1.0.0になるまで7年かかっています。最初のバージョンが出て、今Scala CenterのリードをしているSebastienという方が開発をずっと続けているのですけれども、7年越しでようやく1.0.0をリリースできた。もう非常に安定して使えるものになりました。

Scala.jsは基本的にScalaで書かれたコードをJavaScriptに変換する。コンパイルする。そうすることで、どんなWebブラウザ、最近のモダンなWebブラウザのどこでも動くコードを生成できると。

残念ながら僕の専門外ということもあって、今回のトークではiOSとかiPhoneのネイティブアプリケーションの開発については触れませんが、もし詳しい方がいればぜひこの方向にも突き進んでいただけたらなと思います。

バックエンドとフロントエンドの橋渡し

では、サーバーサイドとフロントエンド。すなわちバックエンドとフロントエンドの両方をScalaで書くことができたときに、じゃあどうやってその両者を橋渡しするのか? 当然Web UIを書いたらサーバーからデータを受け取りたいわけです。それを実現するにはどうしたらいいかということを考えていきます。

理想的には、Scala.jsでなにかScalaのコードを書いて、このときリモートのScalaサーバーがあったら、それに対して関数呼び出し(Local Function Call)をして返り値(return value)を得られればよいんですけれども、現実はブラウザとリモートのサーバーインスタンスは別々の場所で動いているので、関数呼び出しという簡単なわけにはいかなくて、どうしてもRemote Procedure Call、つまりリモートにある関数を呼び出すRPCの仕組みが必要になってきます。

RPCでは、単純な関数呼び出しだけではなく、クライアント・サーバーという2つのプレイヤーが間に入ってきます。しかもデータはネットワーク上を流れるので、バイナリフォーマットでなければなりません。

なので、単純にScalaのオブジェクトをサーバーに渡そうとしても、Scalaのオブジェクトをシリアライズしてバイナリに変換して、またサーバー側でそれを処理するには、バイナリデータをScalaのオブジェクトに戻して受け取る。逆にサーバーが何かデータを計算してクライアントに返すときはその逆、同じような方法でデータのシリアライズ・デシリアライズが間に挟まってきます。

RESTとgRPC

既存のアプローチ、RPCを実装するやり方には2種類あります。RESTかgRPCを使う、この2種類が主なやり方です。RESTの場合にはHTTPのエンドポイントを定義するのが決まりです。GET、POST、PUT、DELETEなどのHTTPのメソッドと、それに加えてデータのやりとりをする窓口となるURLを定義します。

このRESTを実装するためのScalaのライブラリ、フレームワークは非常にたくさんあります。例えばPlay FrameworkとかAkka HTTPなどが有名だと思いますが、Finagleの上に作られたFinatraやFinchなどもありますし、日本の瀬良さんが作ってメンテナンスしているSkinny Frameworkというようなフレームワークもあります。

もう一方のやり方はgRPC。これは本当にGoogleにどっぷり浸かったようなフレームワークで、GoogleのProtocol ButterというSchema LanguageでRPCのfunctionやデータ構造(data structure)を定義します。Protocol BufferのProtoファイルを書いてサーバーとクライアントのスタブ――まぁテンプレートみたいなものですね――それを生成することでサーバーとクライアントの実装をできるようにしていく。

gRPCを使うためのScalaのライブラリもいくつかありまして、ScalaPBとかmuScala、akka-grpcなどを使うと、gRPCとScalaの橋渡しをすることができる。

RESTにすべきかgRPCにすべきか

ただ問題なのが、「RESTを使うのか、そもそもgRPCを使えばいいのか?」というところで判断が分かれるんですね。

REST APIを使うと、その歴史はすごいもう長く十何年という歴史があるので、いろいろ使えるフレームワークも多いし、ライブラリとかクラウド側でAWSなどでのロードバランシングなどのサービスが非常に整っているという事情もあります。が難しいのは、RESTのよいデザイン、APIのデザインに至るまでにすごく経験が必要ですし、よいresource orientedなREST APIのデザインに至るまでに時間がかかる。そういう長所と短所がここの右にあるGoogle Cloudのブログでも紹介されています。

一方gRPCを使うと、やはりGoogleのエコシステム、Protocol Buffer周辺のエコシステムにどっぷり浸からないといけないので、なかなか大変になってくる。なにもかもがgRPCだったりProtocol Bufferの世界に移らないとサービスの実装がままならないという状態になってきます。さらにWebフレームワークの実装もたくさんあるために、どのライブラリを選べばいいかというところでなかなか難しい選択に迫られることになります。

(次回につづく)