GitBucket 4.11をリリースしました

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

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

デプロイキーのサポート

CIツールなどで利用するためのデプロイキーを設定できるようになりました。リポジトリの管理画面で公開鍵を登録することができます。

f:id:takezoe:20170322102545p:plain

これによって専用のユーザを作成することなくJenkinsなどのツールがSSH経由でリポジトリに読み書きできるようになります。

デフォルトのアバター画像の自動生成

ユーザがアバター画像をアップロードしておらず、Gravatar連携も無効になっている場合、ユーザ名の最初の1文字とランダムに選択された色でアバター画像が自動生成されるようになりました。

f:id:takezoe:20170324100207p:plain

プライベートリポジトリをフォークした場合のコラボレータ

プライベートリポジトリをフォークした場合、元リポジトリのコラボレータ設定を引き継ぐようになりました。

f:id:takezoe:20170322102555p:plain

これによって元のリポジトリのオーナーやコラボレーターはデフォルトでフォークされたリポジトリにもアクセスできるようになります。もちろんフォーク後にコラボレータの設定を変更することも可能です。

アバター画像をブラウザでキャッシュ可能に

GitBucketが適切なレスポンスヘッダを出力することにより、アバター画像がブラウザにキャッシュされるようになりました。

また、他のアセットについては以下のようにパスにGitBucketの起動時刻が自動的に付与されるようになりました。

<link href="http://localhost:8080/assets/vendors/bootstrap-3.3.6/css/bootstrap.min.css?20170321132510" rel="stylesheet">
<link href="http://localhost:8080/assets/vendors/octicons-4.2.0/octicons.css?20170321132510" rel="stylesheet">
...

これによってGitBucketのバージョンアップ時などにJavaScriptCSSのキャッシュによる動作の不具合を回避することができるようになりました。

リポジトリイベントをフックするための拡張ポイントを追加

プラグインリポジトリに関するイベントをフックするための拡張ポイントを追加しました。

RepositoryHookを拡張し、Pluginsに登録しておくことでリポジトリに関するイベントの通知を受け取ることができます。RepositoryHookには以下のメソッドが用意されているので処理したいイベントに対応するメソッドをオーバーライドしてください。

  • created
  • deleted
  • renamed
  • transferred
  • forked

今回のバージョンではこの他にも以下のような改善が行われています。

  • Jenkins連携のためのGitHub互換APIの改善
  • いくつかのページでのレイアウト崩れを修正
  • デフォルトで外部サービスへのHTTPアクセスを行わないようにした
  • Java9でもコンパイル可能にした

他にも様々なバグが修正されています。詳細についてはIssueの一覧をご覧ください。

ScalatraのWebサイトをGitHub Pagesに移行しました

ScalatraのWebサイトはこれまでIvanが管理している自前サーバ(?)で運用されていたので時折ダウンしていたり、Jenkinsが落ちているとサイトのビルドができなったりとトラブルが絶えませんでした。

ここ数ヶ月もScalatraの最新版である2.5のドキュメントがサイトに反映できていなかったり、サイトが長期間ダウンしていたりと、さすがにこの状況はいかがなものかということで以前から話には上がりつつ一向に進んでいなかったGitHub Pagesで運用する作戦を決行することにしました。

というか自分が手をつけようと思ったのは吉田さんのこのツイートがきっかけだったのですがw

これまでのサイトはJekyllで構築されていたのですが、まず、ブランチで作業されていたhugo版に自分の方で手を入れてテーマやレイアウト、リンク切れなどを修正し、サイトとして最低限公開できる状態に持って行きました。hugoのビルドやScaladocの生成はコミッタの一人である@dozedTravis職人を買って出てくれ、GitHub Pagesにデプロイできるようになり、最終的に@rossabakerDNSを切り替えてもらいscalatra.orgをGitHub Pagesに向くようにするところまで、作業を開始してから約1週間というところでした。

海外の方とOSSで共同開発をしていてもリアルタイムに協力して作業をするということはなかなかないのですが(時差の関係もありますが…)、日々gitterでやり取りしながら作業が進んでいくのは面白い体験でした。Scalatraのコミッタ間のやり取りは以前はIRCが使われていたのですが、gitterを使い始めてから随分敷居が低くなりましたし、こういったやり取りもしやすくなりましたね。

なにはともあれこれでしょっちゅうサイトが落ちてるなんてことはなくなるはずです。今回hugoを触るのも初めてだったのですが、サイトの復旧優先で作業したためまだいろいろおかしなところがあると思います。今後少しずつ直していこうと思います。

Re-engineering Legacy Software

クリスさんがManningで執筆された書籍です。少し前に購入していたのですが、きちんと読めていなかったので英語の本を読む練習を兼ねて読み直してみました。

Re-engineering Legacy Software

Re-engineering Legacy Software

レガシーコードをいかに改善するかという、クリスさんの経験に基づく(?)現場感溢れる内容で、読んでいると勇気を与えられる反面、こちらの心まで痛くなってくる部分が多々ありますw

もちろん全てのシチュエーションに当てはまるものでもないとは思いますが、リファクタリングの価値を説明する際のテクニックや、リファクタリングするかリライトするかの判断基準や実際の進め方、モノリシックなアプリケーションを分割する際のパターンやメリット・デメリットなど、頭ではなんとなくわかってはいるのだけど自分の中できちんと説明できる言葉を持っていなかった部分が言語化されており、今後の判断や説明の際に1つの拠り所になるのではないかと感じました。

コードは書いた瞬間からレガシーになるとも言われますが、そう考えると多くのソフトウェアエンジニアにとって、程度の差こそあれ日常的な業務の大部分はレガシーコードとの戦いということになるのではないでしょうか。レガシーコードと戦っているソフトウェアエンジニアの皆さんには是非読んでみていただきたいです。英語も非常に読みやすいですし、何より書かれてある内容が「あるある」だったり、「なるほど」と思うことばかりで飽きずに読み進めることができました。

ちなみに日本語版も出ています。原著も読みやすいとはいえ、コード主体の技術書と違って摘み読みしづらいということもありますし、やはり英語だとどうしても読むのに時間がかかってしまうのではないかと思います。せっかく日本語版が出ていますので、時間のない方はこちらがオススメです。

レガシーソフトウェア改善ガイド

レガシーソフトウェア改善ガイド

ScalaMatsuri 2017に参加しました

今年もお台場で開催されたScalaMatsuri 2017ですが、今回はスポンサーとしてだけではなく、個人でスピーカーとしても参加させていただき、Scala Warriorについてお話しさせていただきました。

www.slideshare.net

前がScala.js作者のセバスチャンさんの発表で、最近の機能の紹介として取り上げたCommonJSモジュールのあたりなどは丸かぶりしてしまっており、もう少し違った切り口の発表にすればよかったかなと若干反省しています。

また、セバスチャンさんから質問をいただいたり、セッションの後で直接ご挨拶をさせていただいたのですが、Dotty.jsについて質問したところいろいろ話してくださったのですが、2/3くらい何を言ってるのかわからず、相変わらず英語力の不足を感じました。

ScalaMatsuri全体としては、昨年はセッションにやや偏りがあるなぁと感じたのですが、今年はバランスもよく、初日に聴講したセッションはどれも面白いものばかりでした。また、スピーカーとして来日されていたEPFLやLightbendの方々をはじめ、国外からの参加者も多く、ScalaMatsuriが名実共にヨーロッパやアメリカで開催されるカンファレンスに劣らない国際的なカンファレンスに育ちつつあると感じました。

二日目はお昼過ぎから参加したのですが、疲労もあり、セッションは聴講せずいろんな方々とお話ししていました。特にクリスさん(昨年NYでのScala Daysでもお会いしました!)からは面白い話をたくさん伺うことができました。以下の本をおすすめされたので読んでみようと思いますw

Type-driven Development With Idris

Type-driven Development With Idris

土日2日間の開催は体力的に厳しいところもありますが、セッションを聴講するだけでなく、久しぶりにお会いする方々とゆっくり話したり、アンカンファレンスで小ネタを披露したりと、様々な楽しみ方ができるのはScalaMatsuriならではのスタイルといえるかもしれません。

スタッフの皆様、スピーカーの皆様、スポンサーの皆様、そして参加者の皆様、お疲れ様でした。来年のScalaMatsuriまでまたScalaを書いていきましょう。

GitBucket 4.10をリリースしました

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

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

Scala 2.12、Scalatra 2.5、Slick 3.2へのバージョンアップ

Scalaのバージョンを2.12にアップデートしました。これに伴ってフレームワークもScalatra 2.5、Slick3(3.2.0-RC1)にバージョンアップされています。

特にSlickに関してはこれまで使用していたSlick2.xからの変更が大きすぎるため、blocking-slickというSlick3上でSlick2互換のAPIを利用できるようにするライブラリを開発し、これを利用する形になっています。

これに伴って既存のほぼ全てのプラグインはGitBucket 4.10では動作しなくなってしまうものと思われます。

Slickを使用していないプラグインであればプラグインのリコンパイルだけで済むはずですが、Slickを使用しているプラグインは若干コードを書き換える必要があります。プラグイン開発者の方はこちらのプルリクエストを参考にGitBucketのバージョンアップへの対応をお願いできればと思います。

リポジトリビューアでファイルサイズを表示

リポジトリビューアのファイルの内容を表示するビューにファイルサイズも表示するようになりました。

f:id:takezoe:20170222235502p:plain

この他にも様々な改善やバグフィックスを行っています。詳細についてはIssueの一覧をご覧ください。

Playでいい感じにZipkinするライブラリを公開しました

PlayでZipkinでトレースするライブラリには以下のようなものがあるのですが、どれも用途が限られていたり、Playとのインテグレーションが不十分だったりします。

そんな背景もあり、同僚がPlayでいい感じにZipkinを利用できるようにするためのライブラリを社内向けに作成していたので、これをエンハンスしてOSSとして公開してみました。

github.com

まずはbuild.sbtに以下の依存関係を追加します。

libraryDependencies ++= Seq(
  "jp.co.bizreach" %% "play-zipkin-tracing-play25" % "1.0.0"
)

さらにapplication.confに以下のような設定を追加します。

play.http.filters=filters.Filters

trace {
  service-name = "zipkin-api-sample"

  zipkin {
    host = "localhost"
    port = 9410
    sample-rate = 0.1
  }
}

zipkin-trace-context {
  fork-join-executor {
    parallelism-factor = 20.0
    parallelism-max = 200
  }
}

play.modules.enabled  += "jp.co.bizreach.trace.play25.module.ZipkinModule"

filter.FiltersZipkinTraceFilterを追加します。

package filters

import javax.inject.Inject
import jp.co.bizreach.trace.play25.filter.ZipkinTraceFilter
import play.api.http.DefaultHttpFilters

class Filters @Inject() (
  zipkinTraceFilter: ZipkinTraceFilter
) extends DefaultHttpFilters(zipkinTraceFilter)

ここまでで準備完了です。コントローラーでは以下のようにWSClientの代わりにTraceWSClientをDIして使用することでAPI呼び出しのトレースを行うことができます。

package controllers

import play.api.mvc.{Action, Controller}
import play.api.libs.json.Json
import jp.co.bizreach.trace.play25.{TraceWSClient, ZipkinTraceService}
import jp.co.bizreach.trace.play25.implicits.ZipkinTraceImplicits
import scala.concurrent.ExecutionContext
import javax.inject.Inject

class ApiController @Inject() (ws: TraceWSClient)
  (implicit val tracer: ZipkinTraceServiceLike, val ec: ExecutionContext)
    extends Controller with ZipkinTraceImplicits {

  def test = Action.async { implicit request =>
    ws.url("hello-api-call", "http://localhost:9992/api/hello")
      .get().map { res => Ok(res.json) }
  }

}

また、ZipkinTraceServiceLikeのメソッドを使用してAPI呼び出し以外の処理のトレース情報を取得することも可能です。

// 同期処理のトレース
def test1 = Action { implicit request =>
  tracer.trace("sync"){
    println("Hello World!")
    Ok(Json.obj("result" -> "ok"))
  }
}

// Futureによる非同期処理のトレース
def test2 = Action.async { implicit request =>
  tracer.traceFuture("async"){
    Future {
      println("Hello World!")
      Ok(Json.obj("result" -> "ok"))
    }
  }
}

なお、このライブラリは諸事情によりPlay 2.3〜2.5に対応しています。バージョンのよって利用方法が少々異なりますので詳細については各バージョン向けのREADMEを参照してください。

このライブラリはBrave 4(Java向けのZipkinクライアントライブラリ)を使用して実装されています。Brave 4はBrave 3からAPIが大幅に変更されているので、このライブラリはBrave 4の使い方の参考のもなるのではないかと思います。

JDBCレイヤでDBのシャーディングを行うsharding-jdbcを試してみた

DBのデータ量が増えてきた場合の対策の1つとしてユーザIDなどをキーにデータベースを分割するシャーディングと呼ばれる手法があります。これをJDBCのレイヤで実現してしまうsharding-jdbcというライブラリを見つけました。

github.com

sharding-jdbcは中国のdangdang(当当)というEC大手企業が開発したOSSで、SQLをパースし、SQLに含まれるシャードキーを抽出して接続先のデータベースや、参照するテーブルを切り替えてくれるというものです。

使ってみる

まずはpom.xmlに以下の依存関係を追加します。

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>1.4.1</version>
</dependency>

今回は簡単な例ということで、COMPANY_IDというカラムをシャードキーに接続するデータベースを切り替えてみます。以下のような感じでデータソースを準備します。

// 接続先のデータソースを作成
Map<String, DataSource> dataSources = new HashMap<>();
dataSources.put("ds_0", createDataSource(...));
dataSources.put("ds_1", createDataSource(...));
DataSourceRule dataSourceRule = new DataSourceRule(dataSources);

// テーブルごとのルールを作成
TableRule companyTableRule = TableRule.builder("COMPANY")
  .dataSourceRule(dataSourceRule)
  .build();

// シャーディングのルールを作成
ShardingRule shardingRule = ShardingRule.builder()
  .dataSourceRule(dataSourceRule)
  .tableRules(Arrays.asList(companyTableRule, deptTableRule))
  .databaseShardingStrategy(new DatabaseShardingStrategy("COMPANY_ID", new CompanyIdShardingAlgorithm()))
  .build();

DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);

CompanyIdShardingAlgorithmが接続先データベースを決定するクラスになります。シャードキーに使用するカラムは=BETWEENINでの検索しか行うことができません。ShardingAlgorithmではこれらの検索方法に対応したメソッドを実装する必要があるのですが、今回は=での検索しか行わないのでdoEqualSharding()メソッドのみ実装しています。

public class CompanyIdShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<String> {

  @Override
  public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<String> shardingValue) {
    switch(shardingValue.getValue()){
        case "bizreach":
          return "ds_0";
        default:
          return "ds_1";
    }
  }

  @Override
  public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<String> shardingValue) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Collection<String> doBetweenSharding(Collection<String> availableTargetNames, ShardingValue<String> shardingValue) {
    throw new UnsupportedOperationException();
  }
}

あとは作成したデータソースからgetConnection()メソッドを取得してSQLを実行すればシャードキーに応じたDBに対してSQLが発行されます。

その他の機能

sharding-jdbcにはこの他にも以下のような機能があります。

  • シャードキーで、接続するデータベースではなく参照するテーブルを切り替える機能(SQLがリライトされて実行されます)
  • 一意なIDを発行する機能(シャードをまたぐ自動採番のカラムに値を入れるのに使用します)
  • クエリが複数のシャードにまたがる場合にORDER BYGROUP BYを処理する機能(マージ処理と呼ぶようです)

一方で前述の通りシャードキーに対する検索条件に制限がある、異なるシャードをUNIONできないなどの制約もあります。正常にルーティングが行われないSQLの場合でも特にエラーにはならず、SQL中で最初に検出されたシャードキーを使ってルーティングされているように見えましたが、できればエラーになってくれると嬉しいですね。

まとめ

sharding-jdbcJDBCのレイヤでシャーディングを解決してくれるため、既存の様々なORMやデータベースアクセスライブラリをそのまま利用できるという大きなメリットがあります。利用可能なSQLに制限はあるものの、そもそもシャードキーに対してリッチな検索を行う必要はないでしょうし、複雑なレポーティングが必要といったケースでなければさほど困ることもなさそうです。

現時点でシャーディングが不要だとしても、将来的にデータの増加に伴ってDBのシャーディングを検討する可能性があるようであれば、最初からこれを入れておくという手も考えられるのではないでしょうか。