勉強会の資料を作っている

昨年、会社のブログで「Rails複数DBシステムMySQLからPostgreSQL移行物語」 http://www.techscore.com/blog/2014/12/05/rails_db_migration/ という記事を書いた。

エンジニア的に難易度が高く、かつ評価の難しい案件の後日譚だったため、賛否両論だった。

これについては自分自身が「よくやったよなぁ」「ここまでやる必要があったのかなぁ」みたいな両方の気持ちを持っているので、あの記事を読んだエンジニア達が賛否どちらの意見を持つのも正しいと思っている。

私の上司などは「後からわかった事実でベストはこうだったというのは誰にでも出来ることで、我々は当時自分たちの知りうる知識の範囲で最善と判断した選択をしたことに間違いないし、実際それでうまくいった。なのでこの件についてどのような批判があっても耳を貸す必要はない」という結論に至っており、それはそれで我々の立場として正しいよなぁと思ったりもした。

今度「Railsをのりこなせ!」 https://riding-rails.doorkeeper.jp/events/22793 という、いかにもRailsをのりこなしている猛者どもが話をしそうな勉強会にて、この後日譚を整理して話そうとしている。

私としてはこの記事の内容をそのまま話すだけではブログを読めば済む話だし、どう話を膨らますべきか難しいので悩んでいる。今の本業が Ruby on Rails からちょっと離れてしまっているのもちょっと気後れさせるところではある。

資料を作っている最初の段階でどういう資料を作るべきか悩んでしまったので、気持ちの整理にちょっと日記を書いてしまったというのがこの記事の位置づけである。

eclipseで長い文字列書くとき

Javaで長めのJSONとかXMLとかSQLの文字列書くとき

String longXml = ""
+ "<body>\n"
+ "  <div>\n"
+ "    <p>こんにちは</p>\n"
+ "  </div>\n"
+ "</body>\n";

とか書いてからeclipseのコードフォーマッタで整形かけたら

String longXml = "" + "<body>\n" + "  <div>\n" + "    <p>こんにちは</p>\n" + "  </div>\n" + "</body>\n";

こんなふうに一行になって発狂します。

String longXml = ""//
+ "<body>\n"//
+ "  <div>\n"//
+ "    <p>こんにちは</p>\n"//
+ "  </div>\n"//
+ "</body>\n";

こんなふうに、コメントでガード入れるとフォーマッタが勝手に一行にしないので心の平穏が保てます。

ただ、実現したい機能と関係ないところにこだわるあまりくだらないテクニックを使ってる自分へのいらだちのようなものが残ります。

また、フォーマッタのためにこんなコメント入れてるのことを理解しない読み手は多いので、みんなでコードを書くときにはやっぱりいい手ではない気もします。


自分の美学とチームのベターが対立するときは、チームのベターに従いましょう。
自分の美学をチームに説明して、みんながいいねといった時にはそれをチームのベターにしましょう。
みんながいいねと言わないのに自分勝手な美学を自分だけ実践してると、不幸が訪れる。


コードを書いてる時にふとしょうもないことが気になると上記のように思考が発散し、目的のコードはなかなか書かれなくなる。無念。

OpenGLベースのテキストエディタKashiki

KashikiというOpenGLベースのテキストエディタをかれこれ一年ほどチマチマ開発している。

これが動作画面

バイナリ置き場のURL(Windows/Linuxで動作確認済みバイナリがあります(Java8のインストールが必要))
http://kashiki.tombo.in/

ソースコードgithubにあります
https://github.com/mitoma/kashiki

ローカルのgitの記録を見たら2013年05月29日が初回コミットだから、本当にチマチマとした開発だ。それなりに実用できて世に出せるレベルになったら公開して…なんて考えていたら全然開発が進まず今まで来てしまった。自分一人でemacsキーバインドの実装を考えたりOpenGLの基礎から調べて…、なんてやってるのだから一年たってもお察しの通りの完成度だ。

30もとうに過ぎたオッサンが一年近くソースをいじくり回して「むずかしいよう」とかつぶやいてるわけだ。つまり、あんまりな感じなのである。このままでは定年になっても完成する気がしない。
なので、途中でもいいから公開してしまって悩めるオッサンにアドバイスをお願いしたいわけだ。
とはいえ、オッサンがどういうテキストエディタを目指しているのかわからないと「vimでええやん」とか「sublime textこそニューエイジ」とか言われて終わってしまうので、このエディタのコンセプトをかいつまんで説明すると以下のようになる。

  1. テキストターミナルのように文字主体のUIにもかかわらず
  2. 文字はすべてOpenGLのポリゴンで扱われておりでなめらかなアニメーションをする
  3. emacsキーバインド愛する人間が文書入力しやすいエディタ

どうだろう。オッサンとしては一部の人間にはかなりキャッチーなコンセプトだと自負している。
あと、これはコンセプトではないが、Java8 / JOGL で開発しているので Windows/Linux で動作する。多分Macも動作すると思うんだけどMacは持ってないからわからない。
ライセンスはMIT。理由は、今一番敵の少ないライセンスだと思うからである。

困っていることややりたいことはこれからIssueに登録していこうと思ってるけど、だいたいこんなものに悩んでます。

  • ドキュメントの横幅が画面に収まるような自動調節機能(座標の簡単な計算方法が思いつかない)
  • でっかいファイル開くとパフォーマンスがアレ
  • IMEの真面目な対応
  • emacsでいうミニバッファ的なUIを作る(今はファイルを開こうとするとSwingのダイアログが出る!!)
  • プラグイン機構とか

誰かお助けくだされ〜!!

話は変わって、フィギュアスケート。今季はほんとに町田くんがメインストリームにおどりでてきた感じですね。twitter見てても「俺達の町田」という呼ばれ方で親しまれているようで。
羽生くんは、エキシビジョンでネタプログラムを組めるようになれるかどうかがポイントですね。ネタプログラムができてこそ超一流だと思います。

Javaで型安全にプロパティファイルにアクセスするには。その3

ponto (https://github.com/mitoma/ponto)の機能をちょっとずつ増やしています。
githubで一人開発をしていると圧倒的な自作自演感があるのですが、これはこれで結構楽しいです。

最近追加した機能は以下2点

プロパティファイルって、環境毎に切り替えたい値って結構ありますよね。DB接続文字列だったりホスト名だったり。
Ruby on Rails ではそういう時、環境変数RAILS_ENV=production みたいなのを与えてやって、読み込む設定ファイルを切り替えるっていうことをしていて、それが便利なので Ponto にも組み込んでみました。デフォルトでは PONTO_ENV という環境変数によって、プロパティファイルを読み込んで、そのキーに書かれた値を上書きします。

例えば ponto.properties というプロパティファイルを読み込むクラスを生成して PONTO_ENV=production で実行してやると ponto_production.properties というプロパティファイルも読み込み ponto.properties よりも優先してするようになります。

先日追加した、プロパティファイルの値をint, longなどに型変換して取得できる機能の強化です。
今まではプロパティファイルのなかで value._int=hogehoge と記述していたら盛大に実行時エラーになっていたのですが、これじゃやっぱり型安全性に乏しい感あるよなということでコンパイル時に検査をするようにしました。プロパティファイルをクラス生成後に書き換えたり、先の環境変数で読み込んだプロパティファイルで上書きする機能を使うと結局実行時にエラーでるよなという話もあるので、このあたりはどのへんまで検査するかのバランスが難しいです。


あと、コンパイルエラーが出た時mavenだとコンソールにエラーが出ない件が最悪感漂います。うーん。
そもそもいつリリースするか、バージョン 0.0.1 でリリースするか 0.1.0 か 1.0.0 かで悶々しています。

  • その他
    • ウクライナ情勢、調べれば調べるほどロシアあんまり悪くないように思えるし欧米が邪悪な感じする。
    • 高橋選手、怪我で世界選手権欠場。このまま引退となってほしくないですね。
    • 佐村河内先生、ちょっとアウト感漂う会見でしたね。今回、一番割り食ったの高橋選手なのに一言もないのはひどいですね。

Javaで型安全にプロパティファイルにアクセスするには。その2

先日、Javaで型安全にプロパティファイルにアクセスするアクセサクラスを生成するAPTライブラリを紹介しました。
https://github.com/mitoma/ponto

おかげさまで反響は全くなかったのですが、そもそも型安全というには結局プロパティファイルから取れる値全部Stringだし微妙だなーって感も漂っていたので、

setting.key1=value1
setting.key2._int=123
setting.key3._long=123412341234
setting.key4._float=123.123
setting.key5._double=123123.123123
setting.key6._date=2014-01-01
setting.key7._timestamp=2014-01-01 01:02:03

こんな感じで型を指定したらきちんと文字列以外の型で取ってこれるように機能を追加しました!

これで、「プロパティ名のキー指定間違いをコンパイル時にエラーとして検出できる」という意味での型安全に加えて「プロパティファイルの値を宣言した型のオブジェクトとして取ってこれる」という意味での型安全も追加することができました。

折しも、Twitter上では型安全についてSmalltalkを知る人や関数型を志向している人たちによる、なんかよくわからない型安全性とはなんぞ議論が盛り上がっております。

おっさんは、コンパイル時に問題を検出できりゃそれは型安全だな〜ぐらいの適当な気持ちでこの言葉を使っているので間違っているかもしれません。

あと github に上げただけでまだ public な maven リポジトリに上がってないしバージョンは 0.0.1-SNAPSHOT だし、ペーパーウェア感漂ってる。どうしよう。

話は変わるけど高橋大輔選手、現役最後の演技が佐村河内先生のせいでかなりスルー気味な報道になってるのが無念でならない。

Javaで型安全にプロパティファイルにアクセスするには。

皆さんこんにちは、このブログに記事を書くのも随分久しぶりです。

Javaでメッセージや設定を扱うときに使われるPropertiesクラス。皆さん好きでしょうか?
僕は嫌いです。引数が文字列で、型安全じゃないから。
properties.getProperty("setting.key1") みたいにプロパティを取得するとき "setting.key1" というキーが本当にあるのかどうか実行するまでわからないってすごくストレスじゃないですか?

動かない時に、プロパティファイルのキー名とソース中のキー名が同じかどうか確認する作業。最悪ですよね。
動的言語使ってる時に、そういう問題が起きるのはまぁいいんですよ。そういうもんなので。

けど、Java使ってる時にそういう問題が起きるって嫌じゃないですか。普段「コンパイル時に単純なエラーは検出できる〜〜!!」「IDEによる優れた補完によってコード入力の手間は少ないのだ〜〜!!」とか恥ずかしげもなく言ってるJava好きの人間が「プロパティファイルのキー名間違えた〜。再起動しないと…」とか言わざるをえない状況とか、心底クソだと思います。

で、思いましたよ。
Javaでも Resource.setting.key1() のようにプロパティファイルの値取ってこれたら便利じゃね?みたいな。
イデア元はRubyのsettingslogicです。あれはYAMLで記述した設定ファイルから Settings.setting.key1 のように値を取得できるのでこれをJavaでもやりたいわけですよ。
Javaで実現できたらそれってコンパイル時にキー名の間違いとか検出できますよね。型安全ですよね。うーん、人類はぜひこのようにプロパティファイルにアクセスできるようになるべきだ。

で、考えましたよ。
JavaにはAPTってありますよね。これで、コンパイル時にプロパティファイル読み込んで、すべてのキーに対応するメソッドを自動生成してやればいいのでは…、と。

で、作りました。APTで指定したプロパティファイルから型安全なアクセサを自動性するAPTライブラリを。
https://github.com/mitoma/ponto

詳細は github のページを見ていただければだいたいわかるかと思いますが、これで PontoResource.settings.key1() みたいにしてプロパティファイルにアクセスすることができます。

こんなpropertiesファイルに対して

product_name=Ponto
setting.key1=value1
setting.key2=value2
setting.key3=value3
setting.category1.sub1=subvalue

こんなJavaクラスを自動生成しているからです。

public class PontoResource {
  public static String product_name(){ return getProperties("product_name"); }
  public static class setting {
    public static String key3(){ return getProperties("setting.key3"); }
    public static String key2(){ return getProperties("setting.key2"); }
    public static String key1(){ return getProperties("setting.key1"); }
    public static class category1 {
      public static String sub1(){ return getProperties("setting.category1.sub1"); }
    }
  }
  private static java.util.Properties properties = new java.util.Properties();
  static {
    ClassLoader loader = ClassLoader.getSystemClassLoader();
    try{
      properties.load(loader.getResourceAsStream("ponto.properties"));
    }catch (Exception e){}
  }
  private static String getProperties(String key){
    return properties.getProperty(key);
  }
}

うーん、悪そうなクラスですね〜、クラス名とか規約とかに違反してそうですね〜。スタティックおじさんですね〜。
けどプロパティに型安全に、利便性を損ねずにアクセスするためにはこういうのもアリだと思うのです。

ありなのでは〜!!!?

スローテストと戦う

ここ数年 jenkins で rails 案件のテストをまわしながら改善していった日々をどこかでアウトプットしておきたかったので書いておきます。時期的に Advent Calendar とかに参加できればよかったんですが、jenkinsは今年は開催を見送りとの事なので普通の記事で。(というか、最終的にはjenkins特有のテクニックなどはほとんどないです)
本記事では対策の実現方法について具体的に書くのは面倒なので今回は割愛しました。また、具体的な改善時間については記憶に基づくものなので不正確なのでご了承くださいまし。

私たちのプロジェクトの前提と課題

  • ruby1.8系/rails2.3系/MySQL案件
  • railsプロジェクトは1つではなくサブシステムごとに複数ある
  • 複数のrailsプロジェクトは一部コードを共有しており、そこに手を入れると全プロジェクトの再試が必要
  • テストは年々肥大化し、全体のテストをローカルで実施すると1日仕事
  • ほぼすべてのテストにDBが必要。DDLを発行するようなテストも多い

対策その1「DBはインメモリで」
 テストの実施時に時間がかかっていたボトルネックをまず最初に計測すると、データベースがボトルネックになっていることが分かりました。対策として一通りMySQLのパラメーターをテスト向けにチューニングしたりするわけですが、やはり最後はディスクのIOが問題になりました。
 そこでサクッとMySQLのデータディレクトリをtmpfs上に配備しました。これだけでテストの実行時間が当時2時間程度だったのが30分程度になりました。また、MySQLとの接続がtcp接続だったのも地味に遅かったので、Unixドメインソケットでの接続に切り替えました。今後のジョブノードの並列化も見据え、各テスト用のDBをビルドノード上にMySQLインスタンスを構築する方針とも相性がよく、なかなかの改善だった気がします。

対策その2「テストをフェイズ・システムごとに分割する」
 CIにおけるスローテスト改善のための王道です。
 テスト用のジョブをシステム・フェイズごとに分割し、ビルドパイプラインを構築するスタイルに切り替えました。テストを実施するときに「直近の失敗したテストを再試し、すべて通ったら全テストを再試する」という仕組みもいれました。これにより確かに個別のフィードバックは早くなりました。しかし、私たちはIRCを流れる「unitテスト成功→functionalテスト失敗→unitテスト成功」という一連の流れを見たときに、functionalテストがいまだ失敗している事実を見逃してしまうという問題に直面しました。最後に成功という文字列を見たら、問題がすべて解決されたと勘違いしてしまうということですね。本来的には各開発者はIRCに流れるビルドメッセージをたよりにテストの成否を判断するのではなくダッシュボードを見るべきだというのは分かるのですが、現実問題としてダッシュボードを見ない人に対してべき論を語っても問題は解決しません。
 結局私たちはテストをフェイズごとに分ける事をやめました。私たちにとってコミットXがunitテストを壊したのか、functionalテストを壊したのかといった「どこが」壊れたかという情報はあまり重要ではなかったのです。
 壊れているのか壊れていないのか、それが一番重要だったのです。

  • ポイント
    • テストをフェイズ・システム毎に分割すると、システムの状態を判断するのが難しくなる

対策その3「1ノードでの並列化 その1」
 自動テストは本来他のテストケースと依存関係が無いので、並列実行することが出来ます。気をつけないといけないポイントはテストケースから利用するミドルウェアがきちんと分離されている必要があります。
 railsの場合はparallel_testsがあるのでそれを使えばよいでしょう。対策その1を実施した時点で、自動テストはIOバウンドからCPUバウンドな処理になっていたのでCPUの数だけ並列化させることによって劇的にテスト時間の改善ができました。ちなみに、私たちのプロジェクトではCPUの数よりもちょっと多めの並列数の時にもっともテスト時間がみじかくなりました。

  • ポイント
    • CPUバウンドなテストは並列化でテスト時間を改善できる
    • parallel_testsを使えば楽に並列化できる。

対策その4「1ノードでの並列化 その2」
 parallel_testsはテスト起動時に全テストケースを洗い出し、各プロセスにテストケースを割り当ててから実行します。
 つまりテストA,B,C,Dを2プロセスで処理するとき、プロセス1にA,Bを割り当て、プロセス2にC,Dを割り当ててテストを実行します。この時、テストA,B,C,Dがすべて5分で終わるのであればテスト時間は20分→10分と、およそ半分の時間で終わるのですが、A,Bのテストがそれぞれ9分、C,Dのテストが1分で終わるばあいテスト時間は20分→18分になるだけであまりうれしくありません。
 これを解決するには、テストケースを各プロセスに事前に割り当てるのではなくて手の開いたワーカープロセスが都度都度プロセスをブローカーから受け取るようにする必要があります。私たちはparallel_testsを使うのをやめ、xargsと自作のshell_stack*1というファイルベースのシンプルなスタックの仕組みを使ってこれを解決しました。これによって、各プロセス毎のCPU負荷の分散が平坦化されます。
 よりよく平坦化するにはこの対応では不十分で、過去のテストの実行結果の実績からブローカーが渡すテストケースをテスト実行時間の降順で並び替えるという仕組みも入れる必要があります。

  • ポイント
    • parallel_testsの並列化では個々のテストケースのテスト時間に大きな差がある場合に非効率になる場合がある
    • xargsなどのカジュアルな並列処理の仕組みでparallel_testsの抱える問題は改善できる

対策その5「元気玉大作戦(複数ノードでの並列化)」
 書くのが面倒くさくなって来たので駆け足で。1ノードで並列化できたので、今度は複数ノードでビルドを実行します。
 ビルドマシン単体の用途でマシンを多量に調達するのが難しかったので、開発者の開発環境にjenkinsのビルドノードを別VMで同居させて分散させることにしました。開発者がそれぞれの開発環境で全テストを実施するのはすでに非現実的になっていたので、テストはCIにすべて任せるようになっていました。開発環境のマシンはテストを実行することがほとんどなくなりその分リソースに余剰があったので、余剰リソースをjenkinsに割り当てました。

  • ポイント
    • 開発者のマシンリソースに余剰があるなら、それもjenkinsさんにわけてあげよう
    • ミスターサタンがいないと元気わけてもらえないかも…

対策その6「そしてクラウドへ…」
 諸々の事情で開発環境にも余剰リソースがなくなって来たので、ビルドノードにAWSのEC2を使おうというのが今の私たちの状況です。
 cc2.8xlargeという強力なインスタンスを借りて「1ノードで並列化 その2」を適用して頑張っています。いまプロジェクトは16コアで18並列でテストを実行し、テストを35分で全テストを回しきります。直列実行では8時間程度。何も対策していないマシンで普通に実行したら二日ぐらいはかかると思います。
 「継続的インテグレーション入門」ではテストのフィードバックはおよそ1時間以内という指標が示されています。またテストが1時間を越えるようになればEC2上で「元気玉大作戦(複数ノードでの並列化)」を行えるよう稟議書を書く必要に迫られるでしょう。

※EC2上のインスタンスはネットワーク的にAmazonVPCに接続されており、ビルドノードは外部から直接アクセスできないようにしています。jenkinsをEC2上を使ってるという資料を見たとき、それってセキュリティ的に大丈夫なの?どうやってんの?と私も疑問に思った事があるので書いておきます。

  • ポイント
    • 最後は金の力がすべてです

参考