読者です 読者をやめる 読者になる 読者になる

その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Guice で generics の仮型引数の実体 class (ParameterizedType) を上手いこと inject する

java

タイトルだけでは何を言っているかよくわかりませんが,

public class Foo<T> {
    private Class<T> clazz;

    ...

    public void something() {
        // 例えばここで clazz を使って何らか処理をする
        System.out.println(clazz);
    }
}

みたいな class があるときに,ここの clazzT の実際の class (ParameterizedType) を上手いこと inject したいと言う話題です.


結論から言うと,TypeLiteral<T> を使うと所望の動作を実現できます.

public class Foo<T> {
    private Class<T> clazz;

    @Inject
    @SuppressWarnings("unchecked")
    public Foo(TypeLiteral<T> type) {
        this.clazz = (Class<T>) type.getRawType();
    }

    public void something() {
        // 例えばここで clazz を使って何らか処理をする
        System.out.println(clazz);
    }
}

こういう風に TypeLiteral を inject してから,TypeLiteral#getRawType() を使うと ParameterizedType を取得することが可能となります.
でもって,

public class App {
    @Inject
    private Provider<Foo<Integer>> foo;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
            }
        });
        App main = injector.getInstance(App.class);

        main.foo.get().something(); // => class java.lang.Integer
    }
}

というように使うと,コード中のコメントで書いたように class java.lang.Integer,つまり T の実態の class が inject されている様子を見れてめでたしめでたし.

この記事の例では Provider<T> と Constructor Injection でやりましたが,bind() を使う方法でも実現可能でしょう.

補足

この記事の例だと何が嬉しいか良くわからん感じですが,どこかで役に立つタイミングが来るはず…… (というか今日実際に役に立つタイミングが来たのだけれど,その例をそのまま出すわけにもゆかないし,汎用的な例を考えたものの良いのが思いつかなかった)

chat ops について

chat ops について,何が嬉しいのかとか,その辺を上手く言葉にできていなかったので言葉として記しておくこととします.
chat ops のメリット・デメリットなんかは先達によって散々語り尽くされていると思うところですが,自分の思考をまとめるために……


chat ops というのは基本的に「chat を共通のコマンドライン」と見なして,その上で様々な作業,例えばデプロイなんかを行なうことを指すと思います.
「共通のコマンドライン」はチームの知識が詰まった道具箱であり,それを共有することは大きな力になると思います.
その「共通のコマンドライン」で作業することの何が嬉しいかというと,最も大きなものでは「作業の属人化を避けることができる」というものが挙げられるでしょう.
作業の属人化を避けるというのはすなわち誰でも作業可能になるということで,つまりこれは「お願いします脳」に陥ることを防ぐ大きな助けになる事を意味すると思います.
(とは言え,それはそのチャットを介して行われる作業がブラックボックス化されているというだけということでもあり,本当の属人化を防ぐためにはその裏側に対する理解を深める必要があるとも思います.例えばデプロイだったらチャットを介して呼ばれるデプロイスクリプトを読むなど)


ところで,「コマンドライン」といえば入力するだけではなく,その出力を加工して新たな副作用を生むことも可能です.
chat ops でも同様に,能動的にコマンドを発行するだけでなく,チャットに流れる文言を受けて,その内容に基づいて任意の処理を実行させることが出来ます (というか仕組み的には,能動的なコマンド発行に見えているものも,一旦その命令 (メッセージ) がチャットに書き込まれて,その「チャットに流れた命令」を bot やらなにやらが解釈して対応する処理を行っているので当然の話).
例えば,バッチ処理の開始時や終了時にその結果をチャットに流して,そのチャットメッセージに引っ掛けてメールで投げる,みたいなものが考えられるでしょう.

挙げた例はメール送信処理というものでしたが (あまり汎用的な良い例を思いつかなかった……),もっとプライオリティの低い (失敗しても構わないような,しかしあると便利な) 処理であれば,できるだけその処理をプロダクションコードに含めたくないというのが心情というものです.
万が一そのプライオリティの低い処理が失敗してしまったばかりに,他の重要な処理を巻き添えにして死んでしまっては目も当てられません.
そこで,そういった処理の本体はプロダクションコードに含めずに,その代わりにイベントに応じたチャットのメッセージにフックさせることで,他のプログラムに優先度の低い処理を委譲してしまうという方法が考えられると思います.
そうすることで,その「他のプログラム」がもしも失敗してしまっても,処理の本体は巻き添えを食わずに処理を継続することが可能となります.
つまりチャットを,プロダクションコードを一切いじること無く挙動を拡張することを可能にするハブのような存在として扱うことで,変更による破壊から身を守ることが出来るようになるという感覚です.この機構に乗ることによって,プロダクションコードを気にせず安心して挙動を拡張することが可能となるというメリットを享受することができます.

とは言え,何でもかんでもチャット経由で処理させようとすると処理の内容がプロダクションコードと chat ops 用のコードとで分散してしまって良くないので,適切な分離を心がける必要はあると思います.


また,chat ops につきまとう難題として権限分けの話題があると思っていて,ここについては何か良い方法があるのかどうかよく分かっていません.
誰でも彼でもデプロイ出来てしまえばそれは問題ですし,チャットに流れてくる内容をあらゆる所に無権限でフォワードされても困ってしまいます.
User ID で縛るロジックを chat opsスクリプトに仕込むみたいな素朴な方法がパッと思いつきますが,あまりにアドホックというか……
(この辺を上手く扱えるようなチャットのサービスってあるんですかね?)


後,これは当然のことですが,こうしたチャットを介して処理を行なう際はチャットが SPOF になってしまうので,
チャットが死んだ時に運用不能になるような致命的な処理はここでは行なうべきでは無いと思います.


まあ,色々書いた上で身も蓋もない話ですが,コマンドラインでやれば良いものはコマンドラインでやれば良いし,GUI でやれば良いものは GUI でやれば良いし,チャットでやれば良い物はチャットでやれば良いと思います.適材適所で.
chat ops をしたいからと言って,本来不要なメッセージまでチャットに流してしまうのは本末転倒という感じがありますので……

mysql Connector/J の設定で効きそうなやつ

java

mysql Connector/J を使う時に色々効きそう (あるいは効かなそう) な設定項目を調べたのでまとめておくこととします.なお,対象としている mysql のバージョンは5.6 (以降),Connector/J のバージョンは5.1.34 (以降) です.
Connector/J は設定できる項目が多くて迷ってしまうんですが,道標となる資料も幾つかあるのでそれについてもまとめておきます.

何か追記すべき事項があったら適宜追加してゆきたいと思っています.「これも入れたほうが良いのでは無いか」というようなご指摘ありましたらご一報ください.

設定項目

characterEncoding

おなじみの文字コード.要設定.
ちなみに以下の様なテクがある.

Driver doesn't support utf8mb4 for servers 5.5.2 and newer. The driver now auto-detects servers
configured with character_set_server=utf8mb4 or treats the Java encoding "utf-8" passed via
"characterEncoding=..." as utf8mb4 in the "SET NAMES=" calls it makes when establishing the connection.

MySQL Bugs: #54175: Connector/J cannot handle supplemental characters supported by utf8mb4

mysqld のサーバサイドで "utf8mb4" の文字コード設定をしておいた上で,この項目 (characterEncoding) に "utf8" の設定を渡してやると,ドライバがサーバの設定を自動検出して,コネクションを確立するときに "SET NAMES=utf8mb4" を実行してくれるということみたい.

characterSetResults

なぜか忘れられがちな方の文字コードの設定だがこちらも設定した方が良さそう.

alwaysSendSetIsolation

この項目が true になっていると常にトランザクション分離レベルをサーバに送信することになる.
これは非効率的っぽいので false にすると良さそう.

elideSetAutoCommits

この項目が true になっている場合,Connection.setAutoCommit(boolean) が実行された場合に,サーバとドライバの autocommit の state が異なるときにだけ set autocommit=n を発行するようになる.
つまり,Connector/J はデフォルトだと set autocommit=n をついつい発行しがちなのだけれど,この項目が true になっているとむやみにそれを発行することを抑制することが出来るということのよう.

useServerPrepStmts

この項目が true になっていると server side prepared statement を使うようになる.
MySQL (Connector/J) の server side prepared statement はあんま意味が無さそうな上に正しく扱わないと危険 (memory leak にも似た症状が出る) なので有効にするなら自己責任で,という感じがある.
ちなみに Connector/J のデフォルト値は false (昔のデフォルト値は true だったが途中で「アカン」となって false になったという過去があるとのこと).

See Also:

なお, true にした場合,prepared statement が close されずに leak するというバグがあるとのこと.


cacheServerConfiguration

これを true ににすると,mysql サーバの設定をキャッシュするようになる (SHOW VARIABLESSHOW COLLATION の結果をキャッシュしている様子).
これが false だと connection を確立する時に何個かクエリを飛ばしてサーバの状態を確認するようになってしまうので,connection establish のコストを安く済ませる為に true にすると良さそう.
ちなみにこれを有効にするのは Java 界では常識とのこと!

See also:
MySQL Connector/J を利用するときは cacheServerConfiguration=true を設定する - tokuhirom blog

useLocalSessionState

true にすると autocommit やトランザクション分離レベルをサーバに問い合わせずにローカルの情報を元に判断するようになる.
コストを安くするために true にしておくと良さそう.

maintainTimeStats

サーバへの接続が失敗した時などに、より詳細なエラーメッセージを出力する為に,ドライバはアイドル時間の計算などをしており,そのためにさまざまな内部タイマなどが走っている.
この項目を false にするとそれらの処理をバイパスすることができ,スループットが向上するとのこと.

useUnbufferedInput

true にするとサーバからのレスポンスを読むときに BufferedInputStream を使わなくなる.
false にすると recv() システムコールが減るという記述があるが,ホイホイ false にしてしまって良いものかちょっと判断がつかない.攻めてる気がする.

useReadAheadInput

true にすると,サーバからのレスポンスを読むときに最適化されたノンブロッキングな buffered input stream を使うようになる.
useUnbufferedInput と同じく false にすると recv() システムコールが減るという記述があるが,ホイホイ false にしてしまって良いものかちょっと判断がつかない.攻めてる気がする.

jdbcCompliantTruncation

JDBC Connection Overheads
ここに書いているような感じで,false に設定してやると要らんクエリ発行が減る様子.

jdbcCompliantTruncation のドキュメント読んでると *1,false にしてしまったが最後,ヤベークエリが飛んでもスルーされるようになってしまいそうだけれど,STRICT_TRANS_TABLES が有効になっているとその点は防げる感じで,結果的に要らんクエリが減るだけとなった.めでたしめでたし.
STRICT_TRANS_TABLES が有効になっている場合は false にしても良いんじゃないでしょうか.

rewriteBatchedStatements

true になっていると,マルチステートメントの INSERT/UPDATE をまとめるというやつ.
PreparedStatement の addBatch() や executeBatch() を使ってクエリを作っている時に役に立つ.それ以外の時は関係がない.
そうした機能を使うのであれば有効にすると良さそう.ちなみにこの機能を有効にするためには,同時に useServerPrepStmts が false である必要がある (Connector/J のレイヤで Prepared Statement を構築しなければならないため).

See also:
MySQL Connector/Jにおける大量INSERTのチューニング - SH2の日記

cachePrepStmts

HikariCP が有効にすることを推奨している.true に設定することで Prepared Statement のキャッシュを有効になる.

prepStmtCacheSize

HikariCP が設定することを推奨している.Prepared Statement を最大何個キャッシュするかを設定.HikariCP オススメは250.

prepStmtCacheSqlLimit

HikariCP が設定することを推奨している.Prepared Statement の SQL クエリの文字長 (バイト数) の最大.HikariCP オススメは2048.

参考になる資料

MySQL :: MySQL Connector/J Developer Guide :: 5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J

Connector/J の設定可能項目が全て載っているドキュメンテーション.基本的にこれを読んでいくことになる.

MySQL :: MySQL Connector/J Developer Guide :: 5.1.1 Properties Files for the useConfigs Option

同じく Connector/J のドキュメント.ざっくりとした設定例みたいなのが載ってて参考になる.鵜呑みにするとかなり攻めた設定になりそう.

MySQL Configuration · brettwooldridge/HikariCP Wiki · GitHub

HikariCP を使う上で有効にすべき設定が載っている.

JDBC Connection Overheads

Connector/J がウッカリ発行してしまう不要なクエリを減らす法などが書いている.

http://assets.en.oreilly.com/1/event/21/Connector_J%20Performance%20Gems%20Presentation.pdf

Connector/J を使う上でパフォーマンス改善に役立ちそうな情報がまとまっている pdf.Sun 時代の資料だが参考になる.

*1:Should the driver throw java.sql.DataTruncation exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings (MySQL 4.1.0 and newer)? This property has no effect if the server sql-mode includes STRICT_TRANS_TABLES.

HikariCP で leakDetectionThreshold を設定して connection leak を検出する

java

HikariCP のような connection pool を利用する上で犯しがちなミスとして,connection leak (pool から connection を borrow しっぱなしで pool に返却しないこと) があると思います.connection leak を放っておくと pool 内の connection が枯渇してにっちもさっちもいかなくなるため再優先で修正しなければなりません.


connection leak を修正するには,まず connection leak の発生を検出する必要があります.
以前紹介したような,JMX を使って connection の状態をモニタリングする方法 もありますが,この方法では leak が発生した瞬間を検出することが出来ませんし,どこで発生したかもわかりませんからデバッグが難しくなります.
そこでどうするかというと,HikariCP を使っている場合は leakDetectionThreshold を設定すればグッとデバッグが便利になります.

setLeakDetectionThreshold() を用いてしきい値を設定すると,「connection が borrow されてから pool に返却されるまでの時間」がそのしきい値を超えた場合に,HikariCP は error log としてスタックトレースを出力します *1.これによっていつ,どこで connection leak が発生したのかを検出することが可能になります.

コード的にはこんな感じ.

final HikariDataSource dataSource = new HikariDataSource();
...
dataSource.setLeakDetectionThreshold(5000);

こうしておくと,borrow された connection がsetLeakDetectionThreshold() で設定した 5000ms (5sec) を超えても pool に返却されない時にスタックトレースが出力されるようになります.便利! これで connection leak も直したい放題ですね.

ちなみに実環境では,

final HikariDataSource dataSource = new HikariDataSource();
...
if (env.isDevelopment()) {
    dataSource.setLeakDetectionThreshold(5000);
}

という風にしていて,開発環境下でのみ検出を有効にしています.Java で素朴にスタックトレースを取るのは割とコストが高いというのと,leak を潰すのは開発中にあらかた出来るだろう,加えて JMX で connection の状態をモニタリングしているので実際の運用ではそれで十分だろうという判断からです.


connection leak というのは connection pool を利用する上で起こしやすいバグですし,かつデバッグが難しい部類のバグだと感じています.
採用する connection pool を選定する上で, そうした leak の検出やデバッグを容易にする機能が含まれているかどうかは割と重要な要因だと思います.HikariCP にはそういった connection leak のデバッグを助けるオプションが存在するので便利で良いですね.

*1:This property controls the amount of time that a connection can be out of the pool before a message is logged indicating a possible connection leak. A value of 0 means leak detection is disabled. Lowest acceptable value for enabling leak detection is 2000 (2 secs). Default: 0 https://github.com/brettwooldridge/HikariCP

HikariCP で kamipo TRADITIONAL を有効にする

java

さて2015年も暮れになってきましたので,各位に於かれましては常識の如く kamipo TRADITIONAL を有効にしていることと存じます.
kamipo TRADITIONAL とはなんぞや,という方は以下のエントリが参考になりますからご一読下さい.


さて Java で kamipo TRADITIONAL を有効にするテクについては

上記のエントリで紹介したわけですが,21世紀の我々には文明があるので connection pooling というものを使う (あるいは使えと言われる) ケースが多くなっております.

ということで HikariCP を使うわけですが,この時に kamipo TRADITIONAL をどう設定するのかという話題があります.connection を borrow する度に sql mode を set するというのも渋いので,以下のようにしてやるとよろしい.

final HikariDataSource dataSource = new HikariDataSource();
...
// This is "kamipo TRADITIONAL". More strict, healthy, nice.
// https://github.com/kamipo/etcfiles/blob/b8d7f2dc93567cb3de486197952ac8b048641d31/etc/my.cnf#L28
dataSource.setConnectionInitSql("SET SESSION sql_mode = 'TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY';");

HikariDataSource#setConnectionInitSql() を使うと,connection を initial state でセットアップする時にだけ発行する sql を指定できるので *1,ここで kamipo TRADITIONAL を設定するようにしてやると connection が作られるタイミングで kamipo TRADITIONAL が有効になり,以降はその kamipo connection *2 を使い回すことが可能となります.


簡単ですね!

*1:This property sets a SQL statement that will be executed after every new connection creation before adding it to the pool. If this SQL is not valid or throws an exception, it will be treated as a connection failure and the standard retry logic will be followed. Default: none https://github.com/brettwooldridge/HikariCP#infrequently-used

*2:kamipo TRADITIONAL が有効になった connection という意味です.流行させたい語です

HikariCP のステータスを JMX 経由で取得する

java

HikariCP を使っていると,connection の状態をモニタリングしておきたくなると思います.例えば active な connection と idle 中の connection の比率を見たかったり,あるいは connection がリーク (アプリケーションが connection を borrow するだけして返却してない状態) していないかをモニタリングしたい,みたいなケースが挙げられるでしょう.
こうしたものはモニタリングしたくなるのが心情というものですし,モニタリングすべきだとも思っています.


開発中の環境だと HikariCP のデバッグログを出すことでそこから connection の情報を取得することが出来ますが,本番の環境だとそんなのは出さないし (HikariCP のデバッグログは結構量が多い) ,出したとしてもいちいちログを見てやっていくのはダルいので,やはり JMX で見たいという事になります.
そのやり方は至って簡単で,

final HikariDataSource dataSource = new HikariDataSource();
...
dataSource.setPoolName("pool-name");
dataSource.setRegisterMbeans(true);

という風に,dataSource.setRegisterMbeans(true); としてやることで HikariCP のメトリクスが JMX で取れるようになります *1


あとは jolokia なんかを使っている場合だと,

http://server.example.com/jolokia/read/com.zaxxer.hikari:type=Pool%20(pool-name)

という風にアクセスしてやると HikariCP の connection のステータスを取得することができるようになります.


あとは数値を取ってこういうグラフを描画することが可能.

f:id:moznion:20151213013152p:plain

Total の connection の個数を見つつ,その上に active,idle,awaiting な connection の状態を描画することで,connection のステータスが一目瞭然となります (ちなみに18:00 を超えた辺りで total の connection がガクッと落ちているのは再起動したからです).
便利ですね.


See Also: MBean (JMX) Monitoring and Management · brettwooldridge/HikariCP Wiki · GitHub

*1:registerMbeans: This property controls whether or not JMX Management Beans ("MBeans") are registered or not. Default: false https://github.com/brettwooldridge/HikariCP

ねぎま鍋、ほうれん草とツナのマヨネーズあえ

今日も定時退社することに成功しました.嘘です,実際には45分ほど残業をしましたがこれは定時と言っても遜色はありません.最高の事態です.

というわけで自炊にも成功しています.

マグロの切り落としが300gで340円という破格でしたので調達しました.季節も季節なのでねぎま鍋とします.
醤油大さじ3,酒大さじ3,みりん大さじ1,白だし適量という感じで漬けます.ある程度放置.その間に飯でも炊きましょう.

昨日のほうれん草が残っているので,飯が炊けるのを待つ間に酒の友を作りましょう.幸いなことに良いツナ缶もあります.このツナ缶は id:papix さんからもらったものです.

ほうれん草を茹でたものにツナ缶を1缶,マヨネーズ適量,粗挽き胡椒を想像を遥かに超えるくらいガッツリ,醤油を隠し味程度放り込みます.そして頑張って混ぜる.

成果物.

最高に美味い……奇妙にビールが進む,今日はインドの青鬼です.

各界からの反響.

ねぎまに戻ります.長ねぎに焼き色を付けていきます.これは1本分です.

鰹出汁に醤油,塩,酒,みりん,隠し味に味噌を僅かに加えて味を付けた出汁です.

一煮立ちしたら,漬けていたマグロを漬け汁と共に投入し,長ねぎを追います.とろ火でじっくり煮る.煮る時間は適当で良いと思います.あくが出たら適宜取ります.

味がしみたと確信した時が食べごろなので椀などによそいます.

美味い!!!! 飯だけでなく日本酒も進むのです.


明日はこうです.


[追記]

間違えて技術的なブログの方に書いていた……

普段はこういった事はこっちに書いています.

http://moznion.hatenablog.com