presto-client-rubyのメンテナになりました

トレジャーデータでOSSとしてPrestoのRubyクライアントを公開しているのですが、最近諸事情によりこちらをいじる機会が出てきたのでメンテナにしてもらいました。

github.com

rubygems.org

これまでにHTTPのリダイレクト対応やgzip圧縮のサポートなどを追加したのですが、Trinoに対応させてtrino-client-rubyに転生させる作業なども進めていこうと思います。

なお、まだそんなに難しいことはしてないのもあるのですが、Rubyはまるでわからないのでこのライブラリを使っている社内のアプリケーションを含め、Rubyコードはほぼ勘とコピペで書いています。エコシステムもまだいまいちよくわかっていないのですが、とりあえずRubyGemsMavenと違って気軽にpublishできるのはいいですね…。

-WconfオプションでScalaコンパイラの警告を抑制する

Scala 2.13.2で-Wconfというオプションが追加されており、コンパイラが出力する警告を細かくカスタマイズすることができるようになっています。このオプションはScala 2.12系にもバックポートされており、Scala 2.12.13で利用可能です。

www.scala-lang.org

たとえばScalaコンパイラ-deprecationオプションを指定すると非推奨のAPIの使用に対して警告を出力しますが、build.sbtに以下のような設定を追加することで、自動生成されたコードに対しては警告を出力しないようにできます。

scalacOptions := Seq(
  "-deprecation",
  "-Wconf:cat=deprecation&src=src_managed/.*:s"
)

指定方法は-Wconf:<filters>:<action>,<filters>:<action>,...という感じで、フィルタ部分で対象の警告を指定、アクションでその警告をどう扱うかを指定します。

フィルタでは対象の警告を以下のような条件で指定可能です。条件は&区切りで複数指定可能です。また、deprecationの警告に関してはoriginsinceというオプションを使用してさらに細かく対象を絞り込むこともできます。

  • any すべての警告
  • cat=deprecation 警告のカテゴリを指定
  • msg=regex メッセージを正規表現で指定
  • site=my\.package\..* 対象クラスを正規表現で指定
  • src=src_managed/.* 対象ソースファイルを正規表現で指定

アクションではその警告をどう扱うかを指定します。warningとinfoはwarning-summary / ws もしくは info-summary / is と指定することでまとめて表示することもできます。

  • error / e エラーとして報告
  • warning / w 警告として報告(デフォルト)
  • info / i 警告としてはカウントせずに報告
  • silent / s 警告として報告しない

GitBucketで未使用のインポートに警告を出すようにしてみたところ、Twirlが自動的にインポートするクラスとSlickのモデルクラスで必要なインポートが警告になってしまうので以下のような感じの設定を追加してみました。

scalacOptions := Seq(
  "-Wunused:imports",
  "-Wconf:cat=unused&src=twirl/.*:s,cat=unused&src=scala/gitbucket/core/model/[^/]+\\.scala:s"
)

また、-Wconfオプションだけでなく、コード中で @nowarn アノテーションを付与することで警告を抑制することもできるようです。

これまで自動生成コードなどで警告が出すぎてしまうのでコンパイラのlintオプションを有効にするのを躊躇っていた部分もあるのですが、このオプションで不要な警告を抑制できるのでコンパイラによるチェックを積極的に活用できるようになりそうです。

アーセン・ヴェンゲル自伝 赤と白、わが人生

以前読んだベンゲル監督の自伝の日本語版が出版されました。原著も読んでいたのですが、お布施と思ってこちらも購入してみました。

さすがに日本の出版社から出た書籍なので、ペーパーバッグクオリティだった原著と比べると装丁は非常に丁寧で紙質や印刷も段違いです。英語版の方が数は出ると思われるのですが、原著のペーパーバッグ版とさほど変わらない値段でこのクオリティの本が作れるのは一体どういうことなのだろう?と思ってしまいました。一度読んでいるので内容については特に言うことはないです。以下の原著のレビューを参考にしていただければと思います。

takezoe.hatenablog.com

ただ、残念なことに原著では巻末に結構なページ数を割いて掲載されていたスタッツが日本語版ではまるっと省略されています。日本語版ではきちんと印刷用にレイアウトされたスタッツが掲載されているのでは…と期待していただけに非常に残念です。

確かにこの部分を掲載するとページ数が結構増えてしまいそうですし、そもそも原著を読んでいなければこの部分が存在したことはわからないはずではあるのですが、ベンゲル監督の自伝自体ファンアイテムの一種と思われるので、こういった部分もファンにとっては大事な要素なのではないかと思うのですが、翻訳版としてこれはどうなのか…。

まあ、そんなわけで、本書は原著の完全な翻訳版というわけではなく省略されている部分があるのでお買い上げの際はご注意ください!ということを書きたかったのでした。

Classic Computer Science Problems in Java

Classic Computer Science Problems in Java

Classic Computer Science Problems in Java

  • 作者:Kopec, David
  • 発売日: 2021/01/19
  • メディア: ペーパーバック

正月にManningが全品半額セールをやっていたのでなんとなく購入しておいたものですが、読んでみたら思いの外面白かったです。

シンプルな検索アルゴリズムから後半はクラスタリングニューラルネットワークなどまで、シンプルなJavaプログラムで解説されています。まずはジェネリックフレームワークを作成し、そのフレームワークを使用して具体的な問題を解く、という流れになっており、サンプルコードのシンプルさや、各章の最後にあるエクササイズのほどよい難易度(難しすぎない)もあって、自分で実際に手を動かしてみようという気にさせてくれます。ノリ的には「作って学ぶアルゴリズム」とでもいうような感じです。また、Javaのデータ構造や普段あまり使わない数学関係のライブラリについて復習することができたのも予想外のメリットでした。

一方で、扱っているのはプリミティブなデータ構造やアルゴリズムではなく抽象度の高いもので「Classic Computer Science Problems in Java」というタイトルから想像される内容とは若干異なるイメージを持たれる方もいらっしゃるかもしれません。数学的な知識が必要な箇所もあるのですが、この書籍の範囲外ということで説明は必要最小限に留められています。アルゴリズムの理解そのものについては支障はないとは思うのですが、そのような部分を求めてこの本を読むと肩透かしを食らったような感じになってしまうかもしれません。

なお、このClassic Computer Science ProblemsシリーズはJava版以外にもPython版となぜかSwift版がすでに出ているようです。目次を見る限り扱っているアルゴリズムは同じようなので、これらの言語をお使いの方はそちらを読まれるのが良いかもしれません。

Classic Computer Science Problems in Python

Classic Computer Science Problems in Python

  • 作者:Kopec, David
  • 発売日: 2019/03/15
  • メディア: ペーパーバック

とにかく完成度が高く、読んでいて楽しいエンターテイメント性のある一冊でした。普段ビジネスアプリケーションの開発に従事しているとこのようなアルゴリズムに触れる機会はなかなかないと思うのですが、そういった方にこそおすすめしたい一冊です。

Apache SparkをLivy経由でZeppelinから使ってみる

SparkにREST API経由でアクセスできるようにするApache Livyは以前から知っていたのですが、アドホックなジョブを投げるにはやっぱり画面がないと辛いなぁと思っていたところ、ZeppelinにもLivy用のインタプリタがあるっぽいのでこれを使えばZeppelinのノートブックからLivy経由で簡単にSparkにジョブを投げつけることができるのではと思い試してみました。

github.com

github.com

Livy側の設定

LivyはデフォルトではSparkをローカルモードで実行します。Sparkクラスタに接続するには conf/livy.conf にSparkのmasterのアドレスを設定しておきます。Livyによってセッションが動的に生成されるためYARNモードでの実行が推奨されていますが、スタンドアロンクラスタでも動作はします。

# What spark master Livy sessions should use.
livy.spark.master = spark://spark-master:7077

SPARK_HOMEHADOOP_CONF_DIRを指定して起動。

$ export SPARK_HOME=/usr/lib/spark
$ export HADOOP_CONF_DIR=/etc/hadoop/conf
$ ./bin/livy-server start

LivyのURLはデフォルトでは http://localhost:8998 になります。ブラウザでアクセスするとWeb UIが表示されます。

Zeppelin側の設定

特に何も考えずに起動すればOKです。ZeppelinはSparkも内蔵しているので別途インストールする必要がないのは楽です。

$ ./bin/zeppelin-daemon.sh start

ブラウザで http://localhost:8080 を開き、Livyインタプリタの設定でLivyサーバのアドレスを指定しておきます。Livyをローカルで立ち上げている場合はデフォルトの設定のままで大丈夫なはずです。リソースのアロケーションなど必要な設定も行っておきます。

f:id:takezoe:20210222124622p:plain

使ってみる

Zeppelin上でlivyインタプリタを指定してノートブックを作成します。

f:id:takezoe:20210222125102p:plain

適当なScalaコードを実行。

f:id:takezoe:20210222125118p:plain

ZeppelinからアクセスするとLivy側ではこんな感じでセッションが自動的に作られます。同一のZeppelin上で複数のノートブックを作成した場合、セッションは共有されるようでした。

f:id:takezoe:20210222125132p:plain

Sparkクラスタに直接アクセスできない環境でも、ありものを組み合わせるだけでUIからHTTPプロキシ経由でジョブを実行できるようにできるのは便利なのですが、ZeppelinはせっかくScalaで書いていても入力補完が微妙だったりするのが残念な感じなので、IntelliJから直接Livyに投げつけられたりするプラグインがあったりすると便利かもしれないと思いました。ただ、Livyは最近あまり開発が活発ではない感じなのが若干気になるところです。Spark 3対応もmasterブランチにマージはされているもののまだリリースされていないという状態のようです。

Scala Love in the CityでGitBucketの発表をさせていただきました

主催者のOliさんからのDMで急遽 Scala Love in the City で話させていただくことになり、あまり時間もなかったので準備なしでも話せるGitBucketの紹介をさせていただきました。スライドは以下になります。

www.slideshare.net

他のトークScalaでの関数型プログラミングやScala3の話題が多かったと思いますが、具体的なアプリケーションレイヤから見たScalaという観点での話ができればと思い、前半はGitBucketの紹介、後半は8年間開発を続けてきて難しかったこととしてScala 2.12へのアップグレードについて話させていただきました。

なぜScala 2.12へのアップグレードが辛かったのかというと、

結局こういうことで、Scalatraはレガシーフレームワークとして開発者とユーザーが離れ開発が停滞したことでScala 2.12への対応が遅れ、Slickは逆にDBIOによる破壊的変更が導入されたことで移行コストが非常に大きかった、ということが一度に起きたのがScala 2.12のタイミングだったということです。GitBucketは本体だけでなくプラグインもあるのでフレームワークを変更するコストが非常に大きく(Slickに関しては当時変更も検討しましたが)、趣味プロジェクトで開発リソースが限られていたこともあり、どうにかして最小限のコストでこの問題を解決する必要があったのでした。

しかしなんというか、半分Scalaディスみたいな感じになってしまった感もあるのですが、個人的にはScalaはむしろソースコードレベルではよく後方互換性に配慮されており、型安全や関数型プログラミングのメリットを活かしつつスクリプト言語のような記述性で既存のJava資産を活かすことができる、アプリケーションを書くのに適した言語だと思っているので、もう少しそういったポジティブな面も強調すればよかったかなと反省しています。

ともあれ、これまで海外のカンファレンスで何度かショートセッションをさせていただく機会はあったのですが、小田好先生やScala界隈の錚々たるスピーカーの揃ったメジャーな国際Scalaカンファレンスで通常のセッション枠で話させていただくというのは大変貴重な経験になりました。次の機会があれば、今度は昔話ではなく未来の話ができるようネタを仕込んでおきたいと思います。ちなみに発表後のQAセッションでは「会社を作ることを考えたことはないのか?」という質問があったのですが、どうでしょうねw

jgitverでMavenのバージョン番号をGitの情報から自動設定する

sbtではsbt-dynverプラグインを使ってGitのタグ、コミット情報から自動でバージョン番号を付与できるのですが、同じようなことをMavenでやるにはどうするのがいいのかなぁと以前から疑問に思っていたので、どのような方法があるのか少し調べてみました。結論としては以下のjgitverを使うのがよさそうかなという感じです。

github.com

Mavenのextensionとして導入すると、デフォルトでは以下のようなルールでバージョンが使用されます(pom.xmlに記述されているバージョン番号は無視されます)。

  • タグ(x.x.x) -> x.x.x
  • masterブランチ -> x.x.y-SNAPSHOT
  • その他のブランチ -> x.x.y-<ブランチ名>-SNAPSHOT

CIでMavenリポジトリにデプロイするようにする場合でも、タグ以外からデプロイする場合は自動的にSNAPSHOTになりますし、ブランチの場合は自動的にブランチ名がバージョン名に含まれるので別ブランチのSNAPSHOTで上書きしてしまうということもなくなります。

導入方法は簡単で、プロジェクトのルートディレクトリに.mvnディレクトリを作成し、そのディレクトリ内にextensions.xmlを以下の内容で作成します。

<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
  <extension>
    <groupId>fr.brouillard.oss</groupId>
    <artifactId>jgitver-maven-plugin</artifactId>
    <version>1.5.1</version>
  </extension>
</extensions>

これだけでpackage、deployなどのゴールを実行する際にデフォルトのルールで自動的にバージョンが付与されます。mvn validateを実行するとどのようなバージョン番号になるかを確認できます。

$ mvn validate
...
[INFO] jgitver-maven-plugin is about to change project(s) version(s)
[INFO]     io.github.gitbucket::markedj::1.0.16 -> 1.0.16-SNAPSHOT

.mvn/jgitver.config.xmlで細かい挙動の設定が可能です。たとえば以下のようにすると未コミットの変更がある場合にバージョン番号にdirtyというフラグメントが含まれるようになります。

<configuration xmlns="http://jgitver.github.io/maven/configuration/1.1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://jgitver.github.io/maven/configuration/1.1.0 https://jgitver.github.io/maven/configuration/jgitver-configuration-v1_1_0.xsd">
    <useDirty>true</useDirty>
</configuration>

また、以下の設定を追加するとmasterブランチの代わりに使用するメインブランチのブランチ名を指定することができます。

<nonQualifierBranches>main</nonQualifierBranches>

設定方法の詳細についてはjgitverのドキュメントを参照してください。

バージョン番号にコミットIDを含めるオプションもあるのですが、このオプションが有効な場合でもSNAPSHOTバージョンの場合はコミットIDを含まないという仕様のようです。このあたりもうちょっと柔軟性があるといいなと思いますが、通常のユースケースでは概ねjgitverの標準の挙動で問題ないのではないかと思います。

ところでこのjgitverの作者のMatthieuはGitBucketの初期の頃によく手伝ってくれていたコミッタだったりします。当時は仕事でGitBucketを使っていたようで、Scalaは書けないとのことでしたがw ユーザサポートを手伝ってくれたり、プラグインの紹介サイトを作ってくれたりしていました。数年ぶりに今度は自分が彼のOSSのお世話になっているというのはなかなか面白い巡り合わせだなと思います。OSSのエコシステムに改めて感謝ですね。