2020年の振り返り

今年も今日で仕事納め(の予定)ということで、いつも通り1年を振り返ってみたいと思います。

f:id:takezoe:20201230021206j:plain

リモートワークへの移行

今新型コロナウィルスの影響で3月頃から完全にリモートで作業をしています。同業他社の皆さんも似たような状況かとは思いますが、元々トレジャーデータでは地理的にチームが分散しておりリモートのメンバーと働くことに慣れていること、必要に応じてリモートでも業務が可能な環境が整備されていたこともあり、仕事のしやすさという意味ではそこまで大きな影響は感じませんでした。

とはいえ、自宅の環境面でストレスが溜まることはやはり多く、会社としても今後完全にリモートファーストに移行する流れなのでもう少し本格的に自宅環境を整備してもよいかなと思っています。また、電車通勤がなくなったのは素直に喜ばしいのですが、運動不足が深刻なので少し意識的に体も動かしていきたいところです。

仕事関係

上記の新型コロナウィルスの影響による環境の変化は勿論のこと、社でも方針転換があったりしましたが、自分の業務内容にはそこまで大きな影響はなく今年も引き続きPresto周りを中心に仕事をしていました。

大きなところでは昨年から進めていた某マイグレーションプロジェクトをようやくリリースすることができました。実作業で言えば2〜3ヶ月程度で行けたような気がするのですが、途中に色々あってなんだかんだで結局1年かかってしまいました。もう1つ、すでに着手してからすでに2年が経過しているプロジェクトがあり、こちらも年内に終わらせる予定だったのですが、諸事情により来年に持ち越しとなり晴れて3年目に突入してしまいました。来年には必ず終わらせたいです。

あとは社内の管理系のアプリケーションでScala.jsを結構本格的に使い始めました。詳しくは今年のScalaMatsuriでのtaroleoさんの発表で紹介されています。Scala.js自体はちゃんと動くのですが、主にライブラリの部分などでやはりScalaと全く同じ感覚では書けないなぁと感じる部分があります。

11月にはオンラインで開催されたPresto Conference Tokyo 2020で仕事絡みの発表をさせていただきました。トレジャーデータでのPrestoのリグレッションテストに関する取り組みについて紹介させていただいたのですが、この取り組みはDBのテストに関する論文を読んだりするきっかけにもなったのでよかったです。

takezoe.hatenablog.com

OSS関係

OSS関係はさらにアクティビティが低下してしまいました。新しいことといえばMetalsをちらほらいじっていたくらいでしょうか。

takezoe.hatenablog.com

あとはScalaで書いたCLIツールをネイティブコンパイルしたいなと思い、GraalVMやscala-nativeを試したりしていました。結論としては単にScalaで書いたアプリケーションをネイティブコンパイルするのであればGraalVMを使うのが良さそう。scala-nativeはScalaでCを書くためのものという感じで、ちょっとユースケースが違いそうです。

takezoe.hatenablog.com

scala-nativeに関しては(実用的かどうかはさておき)Modern Systems Programming with Scala Nativeという本がとても面白かったです。

takezoe.hatenablog.com

既存のプロジェクトでは、GitBucketは今年は2回だけのリリースとなりました。リリース回数は少なかったものの、新コミッタであるonukuraさんがかなりアクティブにコミットしてくださり、特にWeb APIカバレッジが劇的に向上しました。

takezoe.hatenablog.com

ScalatraはScala 2.13に対応した2.7.0をリリースしました。また、毎年ドメインの更新時に話題に上がっていたのですが、ついにscalatra.orgのドメインを自分の方で引き取りました。Scalatraに関してはさすがにこれからガンガンやっていく感じにはならないと思いますが、今後も緩々とメンテナンスを続けていきたいと思います。

takezoe.hatenablog.com

Apache PredictionIOは残念ながらAttic行きが決定してしまいました。MLOpsは熱い分野でKuberenetes絡めて有力プロダクトが次々と出てきましたし、自分たちも転職してしまったりSalesforce社の主要メンバーもアクティブではなくなってしまったりという状況だったので止むを得ないですね。しかしこれでClickに続いて二度目のAttic行きを経験することになるとは…。

来年の抱負

今年は兎にも角にも新型コロナウィルスの影響が大きかったですが、いずれにしても今後もリモートワークが続くことになりそうなので前述の通り自宅の作業環境をもう少しちゃんと整えたいなと思っています。

また、通勤時間がなくなったことで運動不足はもちろんですが、ポッドキャストを聞いたり読書をしたりといった時間が大幅に減ってしまった実感があるので、PCで作業する以外のインプットの時間も意識的に作っていきたいところ。OSSもここ2年ガクッと活動量が落ちてしまったので来年は少し復活させていきたいです。

まだしばらく先が読めない感じなので中々目標なども立てにくい感じですが、まあこればかりは頑張ってどうにかなるものでもないので、来年はあまり気張らず必要以上に消耗しないようやっていければと思います。

SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム

だいぶ今更感がありますが、現職では業務における運用作業の比率が割と高めなこともあり、SREのなんたるかを理解できればと思い読んでみました。分量と読み辛さが相まって、和訳版にも関わらず読み終わるのにかなり時間がかかってしまいました。

もちろん前職もWebサービス企業だったのでサービスにタッチしていた頃は運用にも関わっていたのですが、立ち上げ時期のサービスだったこともあり、とにかくできることをやるという感じで運用のための組織的な仕組みをどう作るかというのはあまり深く考えたことがありませんでした。サービスとして存続できるかどうかもわからないうちに継続的な運用を考えても仕方ないという話もありますし、大きな会社だともともと全社的な仕組みがあって新規サービスでもそれに乗っかれたりもしますが、まだそういう感じでもなかったです。

で、この本なのですが、まずとにかく読みにくい…。1冊の書籍というよりは論文集という感じなのですが、そのせいで話題がだいぶ散漫になっている感じがあり(分量が多いのもあるのですが)、中には分散システム入門みたいな話もあったりして、いったこれは何の本なんだっけ?となることもありました。それと翻訳が結構厳しめで、元の英文を想像しながら読まないと意味がよくわからないような箇所もありました。参考になりそうな部分だけつまみ読みしようにもそういう構成になっておらず、全部通して読んでみないとどこに何が書いてあるのかよくわからないというのも読んでいて辛いところでした。

内容的にはGoogle内部のエンジニアリングの様子を垣間見れるという意味では貴重かと思いますし、特にオンコールに入るまでの教育周りなどや新サービスのローンチを効率化するための取り組みなどは参考になりました。ただ、SREというのは基本的には「ソフトウェアエンジニアが運用を行うとどうなるか」という話と認識しているのですが、運用におけるSREとアプリケーションエンジニアとの役割分担など、これはあくまでGoogleの事例集という感じで、実際に現場においてどう適用すればいいのかというのは正直ちょっとこの本を読んだだけではちょっとイメージしづらかったです。

今年になって実践編のサイトリライアビリティワークブックも和訳が出ているようで、こちらを読めばもう少し具体的なイメージが掴めるかもしれないですが、これまた中々の分量なので読む前から若干腰が引けてしまうところです…。

サイトリライアビリティワークブック ―SREの実践方法

サイトリライアビリティワークブック ―SREの実践方法

  • 発売日: 2020/06/15
  • メディア: 単行本(ソフトカバー)

GitBucket 4.35.0をリリースしました

このバージョンにはWebフックが設定済みの場合のみ発生するDBマイグレーションに関するバグおよび標準でバンドルされているnotificationsプラグインに関する互換性問題が存在します。すでに修正版である4.35.3 がリリースされていますのでこちらをご利用ください。

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

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

結局今年は2回だけのリリースでした。リリース頻度は少なかったですが、新コミッタの@onukuraさんのおかげで様々な新機能が実装されたり、Web APIカバレッジが大幅に改善されています。変更内容は多岐に渡りますので、主要な新機能のみ紹介したいと思います。

エディタ、ソースビューアのカラーテーマが選択可能に

エディタやソースビューアのカラーテーマを好みにあわせて選択できるようになりました。エディタのカラーテーマは上部のプルダウンから選択できます。

f:id:takezoe:20201213140343p:plain

ソースビューアのカラーテーマはアカウント設定で選択できます。

f:id:takezoe:20201213140357p:plain

イシュー、プルリクエストの入力補完

テキストエリアでの入力補完がイシューやプルリクエストにも対応しました(#で補完をトリガーできます)。

f:id:takezoe:20201213140410p:plain

クリップボードからの画像アップロード

ファイルアップロードが可能なテキストエリア(イシューやプルリクエスト、コメントなど)で CTRL + Vクリップボードから画像を貼り付けられるようになりました。

なお、この機能はブラウザのClipboard APIを使用しているため、このAPIを実装していない古いブラウザ(Internet Explorerなど)では動作しません。

コミットコメントでの複数イシューのクローズ

コミットコメントでイシューをクローズする際、これまでは以下のように記述する必要がありました。

fix #1, fix #2, fix #3

このバージョンでは以下のようにカンマ区切りで記述するだけで複数イシューを一度にクローズできるようになりました。

fix #1, #2, #3

ファイル編集時にプルリクエストを作成可能に

オンラインエディタでのファイル編集時に直接ブランチにコミットするだけでなく、プルリクエストの作成を選択できるようになりました。これに伴ってリポジトリへのコミット権がないユーザでもファイルの編集を行うことができるようになりました。

f:id:takezoe:20201213140426p:plain

マイルストーンのオーバービュー

マイルストーンのオーバービューを表示するページが追加されました。該当のマイルストーンに紐づけられたイシュー、プルリクエストおよび進捗が一画面で表示されます。

f:id:takezoe:20201213140439p:plain

コミットステータスの表示を改善

GitBucketをCIと連携している場合、各コミットにビルドステータスが表示されるのですが、これまではGitBucketの画面内の限られた場所にしか表示されていませんでした。このバージョンではブランチの一覧やコミットの詳細画面などにもステータスが表示されるようになりました。また、ビルドステータスを詳細は以下のようにツールチップウィンドウで表示されるようになりました。

f:id:takezoe:20201213140453p:plain

Web APIカバレッジを大幅に改善

以下のWeb APIが新規に追加されています。GitBucketでサポートされているGitHub互換APIの一覧はこちらのWikiページを参照してください。

前述の通り、今回のリリースにはこの他にも様々な改善やバグ修正が行われています。詳細についてはIssueの一覧をご覧いただければと思います。また、今回のリリースにはいくつかの非互換な変更が含まれており、標準添付以外の多くのプラグインがそのままでは動作しない可能性がありますのでご注意ください。

GitHub Actionsで重複ビルドを排除する

最近のTravisCIの残念なムーブもあり、GitHub Actionsへの移行を積極的に行なっているのですが、個人的に1つ気になっているのがブランチへのプッシュとプルリクエストに対するアクションでビルドが重複してしまうケースがあることです。

まず、GitHub上で標準で用意されているテンプレートからGitHub Actionsの設定を作成すると以下のような感じになります。

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

この場合、masterにpushしたコミット、およびmasterに対するプルリクエストに対してビルドが実行されます。ただ、TravisCIやCircleCIではmasterブランチ以外のブランチに対するコミットやプルリクエストでもビルドが走っていたのでこれと同じ動作を実現するために以下のように設定を変更します。

on: [push, pull_request]

すると、このリポジトリに作成したブランチからプルリクエストを作成した場合に、以下のようにブランチのpushに対するビルドとプルリクエストに対するビルドで同じコミットに対して2回ビルドが走ってしまいます。

f:id:takezoe:20201204162745p:plain

まあ問題ないといえばないのですが、同じコミットに対して二回ビルドするのは無駄ですし、ジョブが多いとプルリクエストに大量のジョブ実行結果が二重に表示されてしまうので、できれば避けたいところです(ちなみにTravisCIでは同様に重複実行されるのですが、CircleCIでは重複実行されないようになっているようです)。調べてみたところ、以下のように設定されているオープンソースプロジェクトを見かけました。

on:
  push:
    branches: [ master ]
  pull_request:

この設定はコードの変更時は必ずプルリクエストを作成し、マージされたらmasterブランチをビルドするというワークフローであればうまく機能します。とはいうものの、個人のリポジトリでは毎回プルリクエストを作成するのも面倒ですし、逆に業務では複数のリリースブランチを運用したい(masterブランチ以外へのpushでもビルドを走らせたい)というケースも多いのではないかと思います。

どうやらGitHub Actionsで完全にTravisCIやCircleCIと同じ挙動にすることは難しそうな気配なので、次善の策として以下のSkip Duplicate Actionsというアクションを試してみました。

github.com

READMEにも書いてあるように、以下のような感じでpre_jobという重複をチェックするジョブを追加し、もしビルドが重複している場合はmain_jobでifを使ってジョブをスキップするようにします。

jobs:
  pre_job:
    runs-on: ubuntu-latest
    # Map a step output to a job output
    outputs:
      should_skip: ${{ steps.skip_check.outputs.should_skip }}
    steps:
      - id: skip_check
        uses: fkirc/skip-duplicate-actions@master
        with:
          github_token: ${{ github.token }}

  main_job:
    needs: pre_job
    if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running slow tests..." && sleep 30

確かにこのアクションによってmain_jobをスキップすることは可能でした。しかし結果的にスキップはされるものの、ビルドはトリガーされてしまうので、プルリクエストには以下のように複数のビルドが表示されてしまいます。

f:id:takezoe:20201204162808p:plain

というわけでGitHub ActionsでTravisCIやCircleCIと同じ動作をさせつつ重複ビルドを排除するという目的はいまだ達成できていないのでした。何か方法はあるのでしょうか…。

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