Airframe Meetup #3

Taro L. Saito
Airframe
Published in
11 min readDec 2, 2019

2019年に追加されたAirframeの新機能とユースケースについて紹介

AirframeはScalaで高度なアプリケーションを手軽に作成するためのライブラリ集です。

AirframeユーザーのためのAirframe Meetup #3を2019年10月23日に東京で開催しました。当日は季節の変わり目ということもあり、風邪によるキャンセルが多かったのですが(お大事に)、今回は新しい試みとして、AirframeをSpark、Scala.jsなどと一緒に使っていただいている方々によるlightning talkを設けたことで、新しい発見も多く、実りのあるイベントになりました。

嬉しいことに、参加してくれた方によるレポートもあります。

まずは、2019年におけるAirframeのアップデートについて紹介です。Airframeプロジェクトの一環として作成されたScalaの新しいテストフレームワークであるAirSpec、今年リリースされたGitHub ActionsをどうAirframeのCI・リリースに使っているか、Scalaによるマイクロサービスを定義するのに便利なAirframe HTTPの新機能について紹介しました。

AirSpecは、シンプルさを追求したテストフレームワークで、以下のようにAirSpec traitを実装し、public methodをテストケースとして実行する、という2つのルールを覚えるだけで使い始められます。

AirSpecは、AirframeのDIと組み合わせることでさらに高度なテストケースの記述ができるようになります。

まず、Airframe DIでは、Designの中にサービスの起動、終了処理が記述できるlifecycle designの機能が追加されました。

これによりdesignでライフサイクル管理までを含めた再利用可能なコンポーネントを定義できるようになり、このようなdesignを組み合わせて、複雑なサービスを手軽に構築していけるようになっています。

この機能をAirSpecと組み合わせることで、テスト時に必要なサービスの初期化、終了処理を確実に行うことができるようなります。

この機能追加により、Scalaにおけるプログラミング関心が、いかに再利用可能でテストしやすいdesignを定義するかに変わっていきました。lifecycle designを利用することで、サービス実装側のコードもbind[X]の構文だけを使ってモジュールをinjectしていく簡単なスタイルに統一できます。

AirframeやAirSpecのリリースはGitHub Actionsで自動化されています。GitHub Actionsは同時に実行できるタスク数が多いことが魅力で、workflow -> job -> stepの階層を把握し、他の人が書いたワークフローを再利用できるのが魅力です。これによりGitHubだけでCIが完結するようになりました。

AirframeもTravis CIからGitHub Actionsに切り替えることで快適になりました。以下はAirframeでのworkflow定義例です:

またsbt-sonatypeを更新し、数千以上のファイルでも1分以内でSonatypeにアップロード(bundle upload)できるようにしました。詳細は以下のブログで紹介しましたが、この変更により多くのScalaプロジェクトのリリースがこれで短時間で行えるようになっています。

Airframeユーザーによるlightning talkもありました。

島田さんによる「Spark with Airframe」では、Airframeの各モジュールをSparkの運用で上手に活用する手法が紹介されました。

・時系列データの扱いを確実に行うために、airframe-metricsのTimeWindowを利用して区間指定のバグを減らす
・Sparkクラスタ間の通信のために、airframe-codecでオブジェクトをMessagePack形式にserializeし送受信する
・AWS FargateのAPIをSparkの複数ノードから利用してもFargate側がscale outをするまで待てるように、airframe-controlのRetryを利用

airframe-controlには、リトライの待ち時間をランダム化することで、分散ノードからあるサービスに対するリクエストのリトライ間隔が上手にばらつくというjitteringの機能があるのですが、Scala界隈でこの実装を探しても見つからなかったとのこと。exponential backoffでは複数ノードから同一サービスを使うとリトライ間隔に周期性ができてしまうので、exponential backoff + randomizationによるjitteringが有効です。

吉田さんは、GCPのFunctions FrameworkとAirframeのScala.js対応モジュールを使ってサーバーサイドアプリケーションをサーバーレスで構築するためのデモをしてくれました。

・airframe-logでログメッセージを管理
・airframe-diでモジュールの管理(ソースコード)。モジュール定義をまとめることで、アプリケーション側のコードがbind[X]を利用するだけ、と非常に簡潔になっています。
・AirSpecでScala.jsのテストコードを記述。Futureを返すasyncテストがフレームワーク側でサポートされるとさらに使いやすくなるとのこと。

AirframeはScala.jsでも利用できるモジュールが増えてきました。11月にairframe-codecもScala.js対応し、サーバーサイドアプリケーション(Scala + Airframe HTTP)と、クライアント(Scala.js)のオブジェクト通信がMessagePackを通して行えるようになり、gRPCに近いデータ通信の世界観がScalaのインターフェースで実現できるようになってきています。

最後に、竹添さんによるDependency Injectionの歴史、各フレームワークの利害得失のまとめです。Google Guice (JavaベースでScalaと親和性が悪い), implicit parameter、reader monad、MacWire (スコープに全部依存関係を含める必要があるが、コンパイラによるチェックが効く)、Cake Pattern (ボイラープレートが多い、NullPointerExceptionを防ぐのが難しいなどで最近は使われなくなってきたはず)、Airframe DI (コンパイル時チェックはないが、ライフサイクル管理ができて高機能)など、各々の特徴が上手に整理されています。

Airframeは2019年度のロードマップを作成したことにより、目に見える形で順調に機能追加が進むようになりました。

今年のScala Matsuri 2019でもAirframeの開発について紹介したのですが、プロダクション開発の経験から共通部分として使えるコードを見出し、OSSとして抽出していく戦略をとっています。新しいScalaエンジニアがチームに戦力として加わったことで、社内のコードも、AirSpecによるテストケースや、Airframe HTTPを使ったマイクロサービスに置きかわるなど、リファクタリングがどんどん進んでいます。

これからAirframe 20.x.x (2020年) のロードマップを作成する予定ですが、

・Airframe HTTPとScala.jsの融合 (Scala RPC)
・Scala.js 1.0.0対応と、UIライブラリ集 airframe-widget
・CircuitBreaker, RateControlなどマイクロサービスAPIを安全に利用するために必要な機能強化
・SQLによるMessagePackデータ操作のサポート
・AirSpec: Async Testサポート

などが大きな目玉になる予定です。小さなモジュールの開発を積み重ねてAirframeリポジトリに集積していくことで、Webフレームワークなど大きめのライブラリ開発でも行いやすくなった利点を実感しています。OSSとして使い勝手の良いインターフェースを設計するのがAirframeの開発において一番難しい点ですが、今後も大きな成果のために、小さな投資を続けて行きたいと思います。

--

--

Taro L. Saito
Airframe

Ph.D., researcher and software engineer, pursuing database technologies for everyone http://xerial.org/leo