Presto Conference Tokyo 2020でトレジャーデータにおけるPrestoサービスのテスト戦略について発表させていただきました

今年はリモート開催となったPresto Conference Tokyo 2020で、トレジャーデータのPrestoサービスのテスト戦略について発表させていただきました。

スライドはこちらです。

www2.slideshare.net

社内ではpresto-query-simulatorと呼んでいるテストシステムなのですが、基本的な考え方としては現行バージョンとテスト対象のバージョンの2つのPrestoクラスタで実際にプロダクション環境で実行された過去一定期間のクエリを流し、結果を比較するというものです。

2年前に私が入社した時点ですでにこのテストの仕組みは存在していたのですが、さらなる効率・カバレッジ改善と共に、テスト結果の分析に非常に手間がかかる上に分析が個人のナレッジに依存しがちで、結果的にテストを実施しているにも関わらず問題を検出できていないケースもあったため、最近になって結果の自動解析とレポートの自動生成機能を追加し、テスト結果を迅速かつ定量的に評価できるようになりました。

発表では他社さんの類似のテスト事例についても軽く紹介させていただいたのですが、SnowflakeではSnowtrailという内製のテスティングフレームワークが使われているようです。以下のペーパーを読むと同じSaaSならではの苦労やテストの難しさなどかなり共感できる部分があります。

resources.snowflake.com

Snowflakeはプラットフォームとしてタイムトラベル機能をサポートしているので、テストでも実際にクエリが実行された時点でのデータを使ってテストを行うことができているようです。また、SnowflakeSQLでのデータ書き込みができないはずなので、Writeのパフォーマンスや、クエリエンジンによるWriteを駆使したトリッキーなユースケースについて考えなくてもよいであろう点は若干羨ましいところですw

また、TiDBを開発しているPingCAPではKubernetes上でFault Injectionまで含んだテストを行なっているようです。さらなるサービスの安全性と安定性のためにはこういった異常系のテストについても充実させていく必要があるかと思います。

pingcap.com

それからカンファレンス後にTwitterで教えていただいたのですが、Googleでも同様のアプローチでクエリエンジンのデグレードを検出するためのテストを行なっているようです。こちらはVLDB 2020で発表があったようなのですが見落としていました。後で目を通してみようと思います。

なお、今回のPresto Conference Tokyo 2020全体については弊社のサポートチームのKammyさんが執筆された以下のブログ記事に大変よくまとまっていますので、こちらをご参照いただければと思います。

td-support.hatenablog.com

参加者の皆さん、登壇者の皆さん、お疲れ様でした。またいつの日か昨年同様、Prestoクリエーターのお三方やBrianさんもお招きしてオフラインでPresto Conference Tokyoが開催できる日が再びやってくることを願っています。

mockito-scalaについて調べてみた

ScalaTestでは以前からMockitoSugarというトレイトが提供されており、ScalaからMockitoをちょっとだけ便利に使用することができるようになっていたのですが、元々それほど大した機能もなかった上に現在は別ライブラリに切り出されてしまったこともあり、使うモチベーションがだいぶ薄れてしまったのではないかと思います。実際のところ、ScalaTestのMockitoSugarを使わずに直接Mockitoを使っても大差ないのですが、やはりScalaからMockitoを使っていると不便に感じる点があったりします。

Mockito本家ではmockito-scalaというScala向けのライブラリが開発されており、こちらはScala向けにかなり作り込まれているようなので軽く試してみました。

github.com

このライブラリを使うにはbuild.sbtに以下の依存関係を追加します。Mockito本体はmockito-scalaの依存関係で引っ張ってくるので明示的に追加する必要はありません。

libraryDependencies ++= Seq(
  "org.mockito" %% "mockito-scala" % "1.16.0" % "test"
)

基本的な使い方はMockitoSugarArgumentMatchersSugarというトレイトをテストケースにミックスインします。

import org.scalatest.funsuite.AnyFunSuite
import org.mockito.{ ArgumentMatchersSugar, MockitoSugar }

class MyTest extends AnyFunSuite 
    with MockitoSugar 
    with ArgumentMatchersSugar {
  ...
}

もしくはmockito-scalaではトレイトに対応するシングルトンオブジェクトも用意されており、トレイトをミックスインする代わりにシングルトンオブジェクトのメンバをインポートすることで同じように使うことができます。

import org.scalatest.funsuite.AnyFunSuite
import org.mockito.ArgumentMatchersSugar._
import org.mockito.MockitoSugar._

class MyTest extends AnyFunSuite {
  ...
}

mockito-scalaでは以下のようにMockitoをScalaから使う際に便利な様々な機能が提供されています。

  • ScalaTestのMockitoSugarと同じようにmock[T]でモックを生成できる
  • 同様に例外スローや引数のキャプチャもdoThrow[T]Captor[T]のように記述できる
  • mock[MyClass with MyTrait]のようにモックの生成時にトレイトをミックスインできる
  • Mockitoのオーバーロードされた可変長引数メソッドの呼び出しが簡単にできる(doReturn(value, Nil: _*)Java同様doReturn(value)と記述できる)
  • spyLambdaで関数のspyが可能
  • verifyでデフォルト引数や名前渡し(遅延評価)の引数も適切に扱うことができる
  • eqがScalaTestのMatcherと衝突してしまう問題が解消されている(予めeqToが用意されている)
  • Matcherの型変数や括弧を省略可能(any[String]anyStringの代わりにanyと記述できる)
  • null撲滅のためにw Null matcherに警告が出る
  • 引数なしの関数のためにfunction0 matcherが用意されている
  • Value ClassのためにeqToVal matcherが用意されている
  • ArgumentCaptorの代わりにシンプルかつValue ClassをサポートしたArgCaptorが提供されている
  • MockitoのStrict Stubsのサポート(Idiomatic Syntaxではデフォルト)
  • ScalaTest用の便利トレイト(MockitoFixtureResetMocksAfterEachTestなど)
  • Answerを関数で記述できるようになっており、Invocationから引数を抽出する代わりに関数の引数として受け取ることができる
  • Idiomatic SyntaxやExpect DSLというマクロを活用したDSLが提供されている

最後のIdiomatic Syntaxですが、たとえば通常のMockitoを使用した次のようなコードがあるとします。

when(aMock.bar) thenReturn "mocked!"
when(aMock.baz(any)) thenReturn "mocked!"

verify(aMock, times(6)).bar
verify(aMock, atLeast(6)).baz(any)  

Idiomatic Syntaxではこのコードを次のような感じで記述することができるというものです。語順が異なるだけでなく、anyの代わりに*が使えたりします。

aMock.bar returns "mocked!"
aMock.baz(*) returns "mocked!"

aMock.bar wasCalled 6.times
aMock.baz(*) wasCalled atLeast(6.times)

Expect DSLはこのIdiomatic Syntaxをさらに変形させたようなもので、Idiomatic Syntaxで次のように記述するところを

aMock.bar wasCalled 6.times
aMock.baz(*) wasCalled atLeast(6.times)

expectから始まるDSLで置き換えることができます。

expect exactly 6.calls to aMock.bar
expect atLeast 6.calls to aMock.baz(*)

先頭にexpectが来るのでverifyしていることがわかりやすいということのようですが、それなら元のMockitoのverifyでいいのではという気がしなくも…。

その他、機能の細かい部分についてはmockito-scalaのREADMEや、mockito-scalaの作者であるBruno Bonannoさんのブログでも開設されています。

medium.com

mockito-scalaはかなり機能豊富でScalaからMockitoを使う際に便利になっている部分も多いのですが、Idiomatic SyntaxやExpect DSLは若干やりすぎ感がなくもない感じがします。Mockitoというより別のモックライブラリを使っている感じというか…。とはいえMockitoの知識なしで使えるかというとそれも微妙な感じなので、結局学習コストが二重にかかるだけなのではという懸念があります。また、マクロが活用されているということもあり、もし今後万が一mockito-scalaのメンテが止まってしまった場合の対応もなかなか大変そうな予感がします。

それ以外の部分については、特にValue Classやデフォルト引数、名前渡し引数のサポートやAnswerを関数と書けるようになっているあたりはなかなかポイント高いのではないかと思うので、独自DSLを使わず基本機能だけ使うのもありなのではないかと思いました。

sbt-native-imageプラグインによるScalaアプリケーションのネイティブイメージの生成

これまでScalaでGraalVMのネイティブイメージ生成機能を使う場合はsbt-native-packagerプラグインを使用していたのですが、最近新たにsbt-native-imageというプラグインがリリースされたとのことで実際に試してみました。

まずは例によってproject/plugins.sbtプラグインを追加します。

addSbtPlugin("org.scalameta" % "sbt-native-image" % "0.2.1")

次にbuild.sbtに以下の記述を追加してプラグインを有効にします。

enablePlugins(NativeImagePlugin)

最低限の準備はこれで完了です。sbt nativeImageを実行するとtarget/native-imageディレクトリに実行可能ファイルが生成されます。

指定可能なタスクやオプションはREADMEで説明されています。たとえばJVMにフォールバックしないネイティブイメージを生成するのであればbuils.sbtに以下の設定を追加します。

nativeImageOptions ++= List(
  "--initialize-at-build-time",
  "--no-fallback",
  "--no-server"
)

sbt-native-packagerと比較して、できることに大きな差はないのですが、やはりGraalVMを事前にインストールしておく必要がないという点は大きなアドバンテージです。また、GraalVMのバージョンをsbtの設定で指定することができるのもビルド環境の差異によるトラブルを減らすのに役立つのではないかと思います。

以前作ったScalaによるネイティブCLIアプリケーション作成用のgiter8テンプレートもsbt-native-imageプラグインを使うように修正しておきました。

github.com

Learning Spark: Lightning-Fast Data Analytics 2nd Edition

Spark 3.0に対応したLearning Sparkの2nd Edition、しばらく前にDatabricks社のWebサイトから無料でダウンロードできるものを入手していたのですが、最近ようやく一通り目を通すことができました。

Learning Spark: Lightning-Fast Data Analytics

Learning Spark: Lightning-Fast Data Analytics

Sparkの基礎からStructured Streamingによるストリーム処理、パフォーマンスチューニング、MLlibによる機械学習、さらにMLflowによる機械学習パイプライン、Delta Lakeによるデータレイクなど最新のトピックまでカバーされ、サンプルコードも基本的にScalaPythonの両方で同一のコードが記述されているという網羅度の高いSparkの入門書です。

個人的にはややわかりづらいStructured Streamingの挙動について図も多用して説明されていたのが好印象でした。また、Sparkは開発が非常に活発であるということもあり、APIにも複数の種類やレイヤーがあるのも入門社にはわかりづらい部分なのではないかと思うのですが、そのあたりも歴史を追いつつ説明されており、Sparkを使い始めるのであればとりあえずこれを読んでおけば間違いないという内容になっていると思います。構築・運用に関する部分は弱いかなと思うのものの、環境依存なところもありますし、マネージドなクラウドサービスを使うという選択肢もあるので妥当なところではないでしょうか。

Spark 3.0での新機能については最後の章にまとめられているのですが、各章でも該当箇所で個別に触れられています。1st Editionは読んだことがないので比較はできないのですが、単にSpark 3.0の章を付け足しただけというわけではなく、全編丁寧にアップデートされている印象です。

なお、冒頭でも触れたとおり、この書籍はDatabricks社のWebサイトから無料でダウンロードすることができます(メールアドレスの登録が必要)。

databricks.com

以前紹介したPresto: The Definitive Guideもそうでしたが、オライリーでは最近こういうパターンが増えていますね。ノベルティなどでオライリー製作の小冊子が配布されているのもよく見かけます。技術書の出版はグローバルで見ても難しい商売だとは思うのですが、オープンソースプロダクトを支援している企業としては広告宣伝活動の一環としてコストを投下できますし、読者としても無料でまとまった書籍の形で情報を得ることができる、オライリーのブランドを活かした興味深い取り組みだと思います。

GitBucket 4.34.0をリリースしました

f:id:takezoe:20200726033019p:plain

Scalaで実装されたオープンソースのGitサーバ、GitBucket 4.34.0をリリースしました。

https://github.com/takezoe/gitbucket/releases/tag/4.34.0

2020年初、7ヶ月ぶりのリリースとなります。色々変更が溜まっていたり、プラグインのアップデートも必要だったりしたのでいつリリース作業をしようかと思っていたのですが、4連休を使ってリリース作業を行うことができたのでよかったです。次のリリースはいつできるのか…。

今回のバージョンでの主な変更内容は以下の通りです。

システム管理画面の強化

管理者向けのシステム管理画面を強化し、新たな設定項目を多数追加しました。

ファイルアップロードの設定

これまではファイルサイズ、タイムアウトの設定をCLIオプションや環境変数などで行うことができましたが、管理画面上から行えるようになりました。また、別途ラージファイル向けの設定も可能になっています。

f:id:takezoe:20200726032942p:plain

リポジトリ操作の制限

リポジトリのリネーム、削除、フォーク、移管といった操作を管理者のみに制限できるようになりました。

f:id:takezoe:20200726032954p:plain

ユーザ定義CSS

管理者はGitBucketの外観をカスタマイズするために任意のCSSを定義できるようになりました。定義したCSSはGitBucketのすべてのページに適用されます。

f:id:takezoe:20200726033006p:plain

リポジトリビューアの性能改善

リポジトリ情報のインメモリキャッシュや、ディレクトリ内のファイルが多い場合に詳細情報の表示をスキップするなどの対応によって巨大なGitリポジトリでのリポジトリビューアの表示速度が大幅に改善しています。

スタンドアロンモードでのHttpSessionの永続化

組み込みJettyを使用したスタンドアロンモードで使用する場合、これまでは再起動するとHttpSessionはリセットされていましたが、以下のように--save_sessionsというオプションを付けて起動することでHttpSessionをディスクに永続化できるようになりました。

$ java --save_sessions -jar gitbucket.war

Web APIのアップデート

List commits APIが新たに追加されました。また、既存のAPIGitHubとの互換性向上のためいくつか更新されています。GitBucketのユーザさんからはJenkins連携でのトラブルで質問をいただくことが多いのですが、今回のバージョンアップで状況が改善することを期待しています。

プラグインのアップデート

デフォルトでバンドルされているプラグインのうち、以下の2つのプラグインがバージョンアップしています。

今回は7ヶ月ぶりのリリースだけあり、この他にもMariaDBサポートの改善、アクティビティログの改善、サイドバーに表示されるリポジトリ一覧の制限など、様々な改善やバグ修正が行われています。詳細についてはIssueの一覧をご覧いただければと思います。

ThinkPadキーボードをMacで使うためのKalabiner Elementsの設定 最新版

過去にも同じ記事を書いたのですが、ThinkPadキーボードのモデルチェンジやKarabinar Elementsのバージョンアップで微妙に変わっている部分もあるのでThinkPad TrackPoint Keyboard IIで行った設定を自分の備忘録を兼ねてメモしておきます。

スペースキー周辺のキーはなるべくMacBook本体の配列にあわせるようこんな感じに。実際はCapsLockとControlの入れ替えはFor all devicesで全てのキーボードに適用されるようにしています。Mac側の設定で変えることもできるのでそれでもよいと思います。

f:id:takezoe:20200711010515p:plain

日本語入力の切り替えはコマンドキーの単体押しで切り替えられるように。

f:id:takezoe:20200711010635p:plain

トラックポイントのボタンの設定もカスタマイズできるよう、マウスとして認識されているデバイスにもチェックを入れておきます。

f:id:takezoe:20200711010719p:plain

そしてセンターボタンを無効に。これをやっておかないと、トラックポイントでスクロールしようとしたときにセンターボタンでリンクをクリックしてどんどん新しいウィンドウが開いてしまうという事故が多発します。まあでもこれは人によるかも。

f:id:takezoe:20200711010822p:plain

ちなみに上記はUSB接続の場合の設定で、Bluetooth接続の場合はキーボードとマウスが1デバイスとして認識されるようです。なのでトラックポイントのボタンの設定もキーボードと同じデバイスに対して行います。

f:id:takezoe:20200712033526p:plain

ThinkPad TrackPoint Keyboard IIを買った

f:id:takezoe:20200712133642j:plain

ここ数年メインの作業環境がThinkPadではなくなってしまったため、外付けのThinkPadキーボードを買い替えながら使い続けており、現在使っているBluetooth版のものもまだまだ全然使えるので迷ったのですが、7年ぶりのバージョンアップということでやはり購入してしまいました。オーダーしてから届くまで1ヶ月以上かかりました。日本語配列のモデルだともう少し早く届くようです。

www.lenovo.com

見た目は前モデルと変わりませんが、確かに色々と進化しています。特に前モデルは筐体の剛性の不足によるものか、実際のThinkPadと比べるとカチャカチャというややチープなタイプ感があったのですが、今回は明らかにタッチが変わっており、ThinkPad本体のキーボードと近いタイプ感になっています。筐体の剛性に関しては7列時代の外付けキーボードを含めて最もしっかりしており、今までで最もThinkPadのキータッチを忠実に再現している外付けキーボードと言っていいのではないかと思います。

キーストロールは前モデルと比べるとやや浅くなっているようですが、最近はThinkPadも薄型化しているので、ThinkPad搭載のキーボードもこのくらいのキーストロークなのかもしれません。トラックポイントのボタンはフラットな形状に変更されており、以前のものに慣れていると最初は少し戸惑うかもしれません。特にセンターボタンを手探りで探すのがやや難しくなったように感じます。

接続方式に関しては、前モデルでは有線のUSB版とワイヤレスのBluetooth版に分かれていたのですが、今回は1台でUSB/Bluetooth両対応となっています。USB接続もUSBドングルを使用した無線方式になっており、Bluetoothのペアリングを使わなくても無線で使えますし、無線でもBIOSの操作が可能です。USBドングルは使わないときはキーボード本体に収納しておくことができます。前モデルのBluetooth版ではマルチペアリングができなかったため、複数の機器を切り替えて使う際にはペアリングをしなおす必要があり不便だったのですが、今回もマルチペアリングはできないものの、無線USBとの併用によりUSBとBluetoothで機器を切り替えて使うという運用が可能になっています。

f:id:takezoe:20200712134714j:plain

また、前モデルのBluetooth版ではWindows以外ではFnLockが効かないため、ファンクションキーを使うのにFnキーとのコンビネーションが必要だったのですが(USB版ではコンビネーションなしでファンクションキーが使えました。また、Macでも回避策はあったようです)、今回のモデルではキーボード側でFnLockが効くようになっています。

というわけで、とりあえず前モデルで自分が感じていた不満点は全て解決されていました。お値段は定価だと15,000円以上と相変わらずなかなかな感じですが、トラックポイントは他のキーボードでは代替不能ですし、今回のアップグレード内容を考えると既存のThinkPadキーボードユーザも買い換える価値はあるのではないかと思います。今後しばらく経てば割引クーポンなども出ると思いますし、コンパクトサイズかつトラックポイントのおかげでマウスのスペースも節約でき、手狭な自宅作業環境にもピッタリなのでWork from Homeのお供に1枚いかがでしょうかw

発売後に様々なメディアにレビュー記事が掲載されていましたが、以下の記事が大変参考になりました。素晴らしい熱量のレビュー記事ですw

japanese.engadget.com

また、以下のレノボの方のインタビュー記事も面白かったです。ThinkPadキーボードは50%が日本で売れているとのことですが、逆に言えば日本以外では全然売れてないんだなという。そりゃ新製品の優先度も上がらないわけですよね。

news.mynavi.jp

しかしこれでまた今後数年はアップデートが望めないことを考えると、USB-Cのドングルが欲しかったとか、やはりBluetoothでマルチペアリングをサポートして欲しかったとか、本体のUSB-C端子で有線接続もできるとよかったなど、どうしても欲が出てきてしまいますね…。