Presto: The Definitive Guide: SQL at Any Scale, on Any Storage, in Any Environment

昨年から執筆中という話だったオライリーのPresto本がこの4月に発売になったとのことでゴールデンウィーク中に読んでみました。

[asin:B086R9FHB7:detail]

Kindleでも7000円以上という高価格ですが、実は著者の皆さんが勤務されている米Starburst社のWebサイトから無料でダウンロードすることができます(メールアドレスの登録が必要ですが)。

www.starburstdata.com

内容はPrestoのインストール方法、アーキテクチャ、設定方法や外部連携の方法など、Prestoを使う上で必要になる事項が一通り網羅されています。開発者の方々自ら執筆された書籍だけあってPresto自体のアーキテクチャはもちろんのこと各コネクタも設定方法だけでなく内部構造にも触れられていたり、チューニングの勘所が解説されていたりとオンラインドキュメントとの差別化もきちんとなされており、広範囲をカバーした実用的かつ教育的なPrestoの入門書になっています。

Prestoは開発速度が非常に早いので中にはすぐ事情が変わってしまう部分もあるかもしれなかったり、クラウド全盛のこのご時勢に自前でPrestoを運用しようという方がどれくらいいるのかというあたりが若干謎なところではありますが、Prestoを触るのであれば間違いなく読んでおくべき一冊だと思います。

書籍の中では少しだけですが現在私が勤務するTreasure Dataについても触れられていました。Treasure Dataは初期からPrestoのユーザであること、現在では世界でも有数の規模のPrestoユーザであることが述べられています。ミーハーかもしれませんが、米国の著名なテック企業と並んで自分の勤務する企業の名前が挙げられているのを見るのはなんとも感慨深いものがありますね。これらの企業に肩を並べていけるよう引き続き精進していきたいと思います。

Scala Love Conferenceをライブストリーミングで視聴していました

今年はコロナウィルスの影響でScala Daysが延期されてしまったのですが、Scala Loveポッドキャストを配信しているOliさんの主催で先週末にScala Love Conferenceというオンラインカンファレンスが開催されました。

初開催にも関わらず、2トラック構成で休憩も挟みつつ約14時間の長丁場、小田好先生のキーノートもあり、さらには直前になってJetBrains社の提供により3つめのトラックが追加され、さらに当日はTwitchでのライブストリーミングも提供され事前登録者以外にも多くの方が視聴されていたようです。

自分は開催日を認識した時点ですでに参加登録が終了していたのですが、Twitchでライブストリーミング配信が行われていたので、途切れ途切れではありますがいくつかのセッションを視聴させていただきました。スケジュールは以下の通りで、自分は主にJoyルームのセッションを視聴していましたが、両ルームで面白そうなセッションがあった時間帯はブラウザ二枚で同時に視聴したりしていました。

セッション内容について

Li Haoyiさんのティラミスへの拘りとか、小田好先生のインデント構文への入れ込みようが強く記憶に残っています。Li Haoyiさんはとにかくあらゆる車輪を再発明しまくることで知られていますが、それらのLi Haoyiスタックを使ってScalaを学習するための本を執筆中なのだそうです。相変わらずパワフルですね。

Yokotaさんのsbtのセッション、SebastienさんのScala.jsセッションも今後の展望など参考になりました。Scala.jsの最適化はかなり頑張っていて、JVMと比べると数倍程度遅くなるものの手書きのJavaScriptより速くなることもあるとのことです。Yokotaさんの転職先は…大変気になりますね…。

James Wordさんのセッションはsbt-native-packagerを駆使してパッケージング&デプロイを行うライブデモだったのですが、Dockerイメージの各レイヤをビジュアルに確認できるdiveコマンドを使っていて便利そうだなぁと思いました。

LukaさんのMonoidのセッションもわかりやすい説明で面白かったです。後半はだいぶ眠くなってしまって記憶がほぼないので動画が公開されたらもう一度見直したいです。そういえばZLayer(ZIO用DIライブラリ)のセッションではAirframeも名前が出ていました。

オンラインカンファレンスについて

さて、オンラインカンファレンスのメリットとして現地に行かなくてもよいので参加しやすいという点はあるのですが、やはり時差問題は大きく、特に今回のScala Love ConferenceはEUタイムゾーンで朝から夜遅くまでというスケジュールだったのでEU在住の方以外は完走するのは中々厳しかったのではないかと思います。

自分も最後のScala Nativeのセッションを楽しみにしていたのですが、日本時間の朝まで起きていることができず残念ながら視聴することができませんでした。とはいえ、別タイムゾーンで複数日開催だとそれはそれでキツイので長時間でも1日開催の方がマシなのかもしれません。

また、個人的に重要だなと感じたのは、主催者の方々含め全員がリモートから参加している様子が見えることで、オフラインカンファレンスをストリーミングを視聴しているのとは違って、自分もカンファレンスに参加しているという感覚をちゃんと感じることができたという点です。これは実際にリアルタイム視聴してみての発見ですね。

今回は自宅に引きこもって悶々としているこのタイミングでこのようなイベントを視聴できとても良い気分転換になりましたが、いずれまたオフラインカンファレンスが何の障害もなく開催できるようになっても、引き続きオンラインカンファレンスがコミュニティイベントの1つの形として普及すると良いなと感じました。

まとめ

Scala3、光曲社のムーブ、そういえばScala Centerも気づけばすっかりメンバーが変わってしまいました。今回はコロナウィルスの影響でScala Daysの延期、そしてオンラインカンファレンス開催と予測不可能なことばかりです。2020年はScala業界にとっても節目の年になるのかもしれません。

ふと思ったのですが、ブログ用の写真とか撮れないのはオンラインカンファレンスのデメリットかもしれないですねw

jenvで複数バージョンのJDKを切り替える

仕事でJava 8とJava 11を行ったり来たりしたり、Javaの特定のマイナーバージョンでのみ発生する問題の調査などをしないといけないケースが出てきて、最初は都度環境変数で切り替えていたのですが、切り替え忘れてうっかり別のバージョンでコンパイルしてしまうということが増えてきたのでjenvというツールを試してみました。

github.com

Macの場合はbrewでインストールできます。

$ brew install jenv

jenvはデフォルトでは~/.jenvに設定などを保存するようです。以下のディレクトリを手動で作成しておく必要がありました。*1

$ mkdir -p ~/.jenv/versions

.bash_profileなどに以下を追加。

eval "$(jenv init -)"

JDKbrew caskでインストールできるようなのですが、手元にはすでにインストールしたものがあるのでとりあえずそれを追加してみます。

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (1):
    1.8.0_181, x86_64:  "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home

jenv addコマンドでjenvにJDKを認識させます。

$ jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home

手動でダウンロードしたGraalVMとJava 11も追加してみます。

$ jenv add /Users/takezoe/Downloads/graalvm-ce-java8-20.0.0/Contents/Home
$ jenv add /Users/takezoe/Downloads/amazon-corretto-11.jdk/Contents/Home

これで準備完了です。jenv versionsでjenvが認識しているJDKが一覧表示されます。GraalVMは普通のOpenJDKと区別が付かなくて紛らわしい感じがありますね…。

$ jenv versions
* system (set by /Users/takezoe/.jenv/version)
  1.8
  1.8.0.181
  1.8.0.242
  11.0
  11.0.7
  openjdk64-1.8.0.242
  openjdk64-11.0.7
  oracle64-1.8.0.181

jenv global <version>で使用するバージョンをグローバルに切り替えます。JDKのインストールは自分で行う必要がありますが、一度JDKを認識させてしませば使い勝手はpyenvやrbenvと同じような感じです。

$ jenv global 1.8.0.181

特定のディレクトリだけ別のバージョンを使用するには当該のディレクトリでjenv local <version>を実行します。この場合、ディレクトリに.java-versionというファイルが生成されます。このファイルをGitにコミットしておけばブランチごとに使用するJavaのバージョンが異なる場合でも自動で切り替えることができますね。

$ jenv global 11.0.7

jenvでのバージョン切り替えに応じて環境変数JAVA_HOMEも切り替えるには以下のコマンドを使用してexportプラグインを有効にしておく必要があるようです。

$ jenv enable-plugin export

SDKMANでも複数バージョンのJDKの切り替えができ、こちらはJDKのインストールもできるようですが、ざっとドキュメントを見たところでは「特定のディレクトリのみこのバージョンを使う」という設定ができるかどうかわかりませんでした。ひとまずjenvをしばらく使ってみようと思います。

追記:jenvでJDKディストリビューションの区別が付かないという問題は最新のmasterブランチでは修正されており、jenv addでprovider付きのエイリアスも登録されるようになっていました。ただ、この修正を含むバージョンはまだリリースされていないようで、Homebrewでインストールすることはできません。

*1:追記: 2022年10月現在Homebrewでインストール可能なバージョンでは手動でディレクトリを作成する必要はありませんでした。

Modern Systems Programming with Scala Native: Write Lean, High-Performance Code without the JVM

いまだScala 2.11までのサポートしか提供されていないなど開発が停滞気味のScala Nativeですが、最近書籍が出たということで読んでみました。

仕事でScalaCLIツールを書くことがあるのですが、やはりJVMの起動時間のオーバーヘッドが気になるのでネイティブバイナリを作成可能な技術としてScala Nativeが気になってはいたのですが、最近はそういう用途であればGraalVMのネイティブイメージ生成機能の方が最新バージョンのScalaを利用できたり、使えるライブラリの自由度など様々な面で実用的だなぁと感じていました。が、この本を読んでScala Nativeに対するイメージが一変しました。そう、Scala NativeはScalaで書いたプログラムをネイティブ化するためのものではなく、ScalaC言語として使うためのものだったのです。そう考えるとScala Native対応ライブラリの少なさもデメリットにはなりません。Cライブラリを使えばいいのです。

この書籍ではポインタやメモリ管理、システムコールを活用したプロセス管理、ネットワークプログラミングなど、Scala Nativeによるシステムプログラミングの基礎が解説されています。中盤からはlibuvとlibcurlを使った非同期I/O、LMDBを使ったデータの永続化、最終的には簡単なものですがWebサービスを構築するところまでいきます。本書の中で使用されているScalaライブラリがJSON変換用のArgonautだけという事実がScala Nativeの性格を如実に表しているのではないかと思います。

サンプルコードの中にはScala的に若干微妙なものもあったりするものの、この書籍はそういうところを見るべきではないのは明らかなのですが、とはいえ正直なところ各サンプルを眺めていると果たしてこれはScalaで書く必要があるのだろうかという気持ちにならないこともありません。しかしZoneを使ったメモリアロケーションの自動化などScala Nativeならではの便利機能もありますし、FutureExecutionContextなど既存のScalaの抽象化インターフェースを活用しているサンプルもあったりするので、超高レベルなシステムプログラミング環境として密かに需要があったりなかったりするのかもしれません。

この本を読んでScala Nativeにも頑張って欲しい気持ちが出てきたので、日本時間の昨日から今朝にかけてオンラインで開催されたScala Love Conferenceでのこの書籍の著者であるRichard WhalingさんによるScala Nativeのセッションを楽しみにしていたのですが、残念ならタイムゾーン問題により該当のセッションまで起きていることができませんでした。セッションはすべて録画されているとのことなので動画が公開されるのを待ちたいと思います。

scala.love

みんなのJava OpenJDKから始まる大変革期!

Java 14もリリースされるとのことですが、最近の動向をちゃんと追えていないので学習のためと、最近ずっと自宅から仕事をしているので気分転換の読書も兼ねて最近発売された「みんなのJava」を読んできました。

すでに現場でJavaを活用している人向けに、Javaの最新情報がざっくりと把握できるという感じの内容で、Java 14までの変更点、OpenJDKディストリビューションJakarta EE、Micro Profile、GraalVM、最新のマイクロサービスフレームワークと旬な話題を識者の方々の要点を押さえた解説により短時間で把握することができて助かりました。ここから必要に応じて各トピックを掘り下げていけばよいかと思います。季節ものなので今読んでおくべきと思います。あと、紙が厚いです。

個人的にはやはりJava 14までの新機能、GraalVMが興味深いところです。しかしJava 8の時点ですでに感じていましたが、随分Javaも様相が変わったなぁという感じがします。すぐに最新バージョンを使える現場ばかりでもないとは思いますが、今後も色々と変化がありそうなのでしっかりキャッチアップしていかないといけないですね。

Scala、Picocli、GraalVMによるネイティブCLIツールの作成

以前、Scalaを使ってCLIツールを作るという記事を書いたのですが、やはりJVMベースのアプリケーションには起動時間のオーバーヘッドが付き物なのでちょっとしたCLIツールをScalaで書こうという感じにはなかなかなりません。そこで今回はPicocliというJava向けのCLIツール用ライブラリとGraalVMを使ってネイティブCLIコマンドをScalaで作る方法を試してみました。

github.com

sbtプロジェクトの作成

今回はPicocliのドキュメントで例として掲載されていたchecksumコマンドをScalaで実装してみます。まずは以下の内容でbuild.sbtを作成します。

name := "picocli-scala-example"

version := "0.1"

scalaVersion := "2.12.7"

libraryDependencies ++= Seq(
  "info.picocli" % "picocli" % "4.2.0"
)

checksumコマンドのソースコードはこんな感じです。JavaコードをそのままScalaに書き換えただけです。コマンドやオプションの情報をアノテーションで宣言するのですが、フィールドをvarで宣言しておかないといけないのがちょっと微妙かもしれないですね。

package com.github.takezoe

import java.io.Fileimport picocli.CommandLine._
import java.math.BigInteger
import java.nio.file.Files
import java.security.MessageDigest
import java.util.concurrent.Callable
import picocli.CommandLine

@Command(name = "checksum", mixinStandardHelpOptions = true, version = Array("checksum 4.0"),
  description = Array("Prints the checksum (MD5 by default) of a file to STDOUT."))
class CheckSum extends Callable[Int] {

  @Parameters(index = "0", description = Array("The file whose checksum to calculate."))
  private var file: File = null

  @Option(names = Array("-a", "--algorithm"), description = Array("MD5, SHA-1, SHA-256, ..."))
  private var algorithm = "MD5"

  override def call(): Int = {
    val fileContents = Files.readAllBytes(file.toPath)
    val digest = MessageDigest.getInstance(algorithm).digest(fileContents)
    printf("%0" + (digest.length * 2) + "x%n", new BigInteger(1, digest))
    0
  }
}

object CheckSum extends App {
  val exitCode = new CommandLine(new CheckSum()).execute(args:_*)
  System.exit(exitCode)
}

Annotation Processorの実行

PicoliではGraalVMによるnative-image対応のためにAnnotation Processorを使用したコード生成が必要です。Annotation Processorはpicoli-codegenという別ライブラリとして提供されているのでこれをprovidedで依存関係に追加します。

libraryDependencies ++= Seq(
  "info.picocli" % "picocli" % "4.2.0",
  // Add a line below
  "info.picocli" % "picocli-codegen" % "4.2.0" % "provided"
)

build.sbtに以下のような感じの設定を追加して、processAnnotationsタスクでAnnotation Processorが実行されるようにします。

lazy val processAnnotations = taskKey[Unit]("Process annotations")

processAnnotations := {
  val log = streams.value.log
  log.info("Processing annotations ...")

  val classpath = ((products in Compile).value ++ ((dependencyClasspath in Compile).value.files)) mkString ":"
  val destinationDirectory = (classDirectory in Compile).value
  val processor = "picocli.codegen.aot.graalvm.processor.NativeImageConfigGeneratorProcessor"
  val classesToProcess = Seq("com.github.takezoe.CheckSum") mkString " "

  val command = s"javac -cp $classpath -proc:only -processor $processor -XprintRounds -d $destinationDirectory $classesToProcess"
  runCommand(command, "Failed to process annotations.", log)

  log.info("Done processing annotations.")
}

def runCommand(command: String, message: => String, log: Logger) = {
  import scala.sys.process._
  val result = command !if (result != 0) {
    log.error(message)
    sys.error("Failed running command: " + command)
  }
}

なお、Picocliにはアノテーションを使わないAPIも用意されており、そちらを使用すればAnnotation Processorを使わなくてもネイティブイメージの生成に対応できます。

GraalVMのインストール

続いてGraalVMをインストールします。以下からGraalVMのCommunity Editionをダウンロードして適当なディレクトリに展開し、binディレクトリを環境変数PATHに追加しておきます。

github.com

ネイティブイメージの作成に必要なnative-imageコマンドは以下のようにして別途インストールする必要があります。

$ gu install native-image

ネイティブイメージの作成

sbtでのネイティブイメージの作成にはsbt-native-packagerを使用しました。

github.com

project/plugins.sbtに以下の設定を追加します。

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.6.1")

また、build.sbtに以下の設定を追加し、GraalVMNativeImagePluginを有効にするとともにパッケージ作成時にprocessAnnotationsタスクが実行されるようにしておきます。

packageBin in Compile := (packageBin in Compile dependsOn (processAnnotations in Compile)).value

enablePlugins(GraalVMNativeImagePlugin)

これで準備は完了です。sbtを以下のように実行するとネイティブイメージが作成されます。

$ sbt graalvm-native-image:packageBin

ネイティブイメージはtarget/graalvm-native-imageディレクトリにプロジェクトと同じ名前で生成されます。試しに実行してみましょう。

$ ./target/graalvm-native-image/picocli-scala-example
Missing required parameter: <file>
Usage: checksum [-hV] [-a=<algorithm>] <file>
Prints the checksum (MD5 by default) of a file to STDOUT.
      <file>      The file whose checksum to calculate.
  -a, --algorithm=<algorithm>
                  MD5, SHA-1, SHA-256, ...
  -h, --help      Show this help message and exit.
  -V, --version   Print version information and exit.

こんな感じで動作します。

まとめ

ScalaとPicocliを使用してGraalVMでネイティブイメージを作成できることを確認できました。ScalaCLIツールを書きたいけどJVMの起動時間が気になるという場合には選択肢になるのではないかと思います。また、以下の記事ではdeclineなどScala製のライブラリを活用したGraalVMによるネイティブCLIアプリケーションの作成方法を解説されています。ライブラリの選択など参考になります。

msitko.pl

Scalaでのネイティブアプリケーションの作成にはscala-nativeという選択肢もありますが、活発に開発が行われているとは言い難い状況で現時点ではScala 2.11のサポートに留まっています。また、利用可能なライブラリもかなり限られており、実用にはちょっと厳しいかなという感じがあります。

この記事で試したプロジェクトは以下のGitHubリポジトリに置いてあります。

github.com

Scalatra 2.7.0をリリースしました

Scala用のSinatraライクなWebフレームワークScalatraの最新バージョン2.7.0をリリースしました。

github.com

今回は2年以上ぶりのメジャーバージョンアップですが、今回はScala 2.13への対応、メンテナンス性向上のためにあまり使われていないモジュールやScalatra 2.6系ですでに非推奨になっていた機能の削除がメインとなっています。

Swaggerサポートに関してはいくつかの修正や機能追加が行われています。また、TwirlおよびScalateサポートにはScalatra Formsを便利に使用するためのヘルパーが追加されています。Scala 2.13のサポートにあわせて本体のコアクラスにもいくつか手が入っている箇所がありますが、Scalatra 2.6.0で稼働していたアプリケーションは非推奨機能を使用していない限り、ほぼそのままScalatra 2.7.0でも動作するはずです。

詳細な変更内容は以下のアナウンスを参照していただければと思います。 https://scalatra.org/2020/03/01/2020-03-01-scalatra-2-7-0-released.html

当初やSwagger 3.0のサポートやJSONサポートの改善なども計画していたのですが、なかなか手が回りませんでした。今後についても現在の自分の状況やコミッタのリソースを考えるとなかなかアクティブに開発を進めていけるとは言い難い状況ではありますが、すでに安定したフレームワークですので最新のScalaのバージョンへの追従をメインに引き続きメンテナンスを継続していこうと思います。