#jjug_ccc #ccc_r16 JJUG CCC 2014 Fall 参加レポート / R1-6 Concurrent Mark-Sweep Garbage Collection 再入門

※この投稿は、丸太式 Advent Calendar 2014の9日目です

今日はJJUG CCC 2014 Fallの参加レポート3本目です。

概要

R1-6 Concurrent Mark-Sweep Garbage Collection 再入門
by 久保田 祐史さん (@sugarlife)

メモ

CMS GC

  • GC: 不要なメモリ領域を探し、解放して再利用可能とする
  • Parallel GC: アプリケーションを止めて動く。GC時間は短いが、応答しなくなる
  • Concurrent GC: アプリケーションと同時に動く
  • Concurrent Mark-Sweep GC: ゴミじゃない領域をMarkし、Markされていない領域をSweepして再利用可能にする
    Compactionしないので、メモリ空間が断片化する

HotSpot VMのCMS GC

  • Heap -> New Gen.(Eden, Survivor0, Survivor1), Old Gen.(Tenured)
  • Minor GC: New Gen.に対してParallel Copy GC: アプリケーションを止めて、メモリが断片化しないようにGC
  • Major GC: Old Gen.に対してCMS GC

  • Minor GC: Eden -> Survivor 0, Survivor 0 -> Survivor 1

  • Promotion : Survivor -> Tenured
  • Major GC: Old領域の占有率やHeap使用量増加速度によって発生する
  • CMS GCは安定していない。失敗してSTW:Stop The Worldを伴うFull GCが発生することがある

  • Full GC (with STW): 全領域がGC対象
    Promotion failed, Concurrent mark failed

GCログの読み方

  • タイムスタンプ: Java processのuptime (sec.)
    -XX:+PrintGCDateStampsで、読める時間表記で出力
  • Minor GC: ParNew GC前のNew領域サイズ->GC後のNew領域サイズ(総容量)
    かかった時間は後ろの方に出るtimes (real)を参照
  • Minor GC (promotion): PromoteされたサイズはNew領域の差分 – 全体の領域の差分

  • CMS GC: initial mark
    STWをかけて、ルートオブジェクトから直接たどれるオブジェクトをマーキング

  • concurrent mark
    アプリケーションを再開しつつ、initial markでマーキングされたオブジェクトからたどれる全オブジェクトをマーキング。
    処理中に生成・変更されたオブジェクトはDirty Cardにされる
  • cuncurrent preclean
    STWが発生するRemarkを短縮するため、concurrent mark中にDirty Cardのオブジェクトをマーキング
  • concurrent abortable preclean
    Minor GCによってEden使用量が2MBになるまで最大5秒待つ
  • Remark (Final mark)
    STWしてNew領域を全スキャンして、New領域からOld領域を参照しているものにmark
  • concurrent sweep
    未マークのオブジェクトを改修
  • concurrent reset
    次のCMS GCで使う関連マップをクリアして、initial markの処理にゴミを残さない

注意すべきパターン

  • Full GC: promotion failed: Minor GCでOld領域にPromoteできなかった場合に発生
    断片化を解消するためのFull GC

    • 短命オブジェクトはNew領域で改修するべき(断片化を防ぐ)
    • New領域のサイズを調整する
    • PromoteさせずにNew領域で粘る
    • 兆候: Minor GC後のOld領域より、次のMinor GCの前のOld領域が大きい->短命オブジェクトがOld領域に飛んでる
    • 兆候: Old > NewだがPromotion Failed: Newに大きいオブジェクトしか入っていない
  • Full GC: concurrent mode failure: CMS GC中にもう一度CMS GCが発生しようとした場合

    • CMS GCの改修が間に合っていないので、開始タイミングを早める
    • サーバのスペックをあげる
      • 単純にいメモリを増やすより、CPUスペックをあげるべき
  • GC locker: GCを止める機構。HotSpot初期化や、JNI API呼び出しの一部

  • GC locker: Trying a full collection because scavenge failed
    • GCが止まっているのでNew領域にはいらずOLD領域に割り当て
    • 深刻な断片化、Promote失敗
  • 対策
    • GC Lockerの頻度を下げる
    • 断片化しないGCを使う
    • 短命JVM

自分用Mavenリポジトリを作る

※この投稿は、丸太式 Advent Calendar 2014の8日目です

自分で作った物のJAR(fat jarではない)をMavenリポジトリにデプロイしておけば何かと便利な気がしたのと、ExCellaをMavenで管理できないのはやっぱり不便なので、自前のMavenリポジトリを用意してみました。

必要なもの

  • Javaの実行環境
  • 適当なサーバ
  • Nginx等のフロントサーバがあるといいですね

やること

  • Sonatype NEXUS OSSをダウンロード
  • 適当なディレクトリに展開
  • 展開したディレクトリで、以下のコマンドをを実行
    % bin/nexus start
    ※Windowsならbin\nexus.bat startで良いんじゃないでしょうか
    フォアグラウンドで実行するなら、startの部分をconsoleにすれば良かったはず。

NetBeansでpersistence.xmlを生成してHibernateを使ったらはまった話

※この投稿は、丸太式 Advent Calendar 2014の7日目です

現象

NetBeans 8.0.1でJPAの持続性ユニットをHibernateを使うようにして作成すると、EntityManagerFactoryでEntityManagerを作れない。
※Persistence Unitを正しく指定しても、見つからない旨の例外が発生する

原因

NetBeansが生成するHibernate向けのpersistence.xmlの内容で、providerorg.hibernate.ejb.HibernatePersistenceが設定されるが、このクラスは@Deprecatedでありなにやら挙動が違うらしい。

対応

persistence.xmlのproviderに、org.hibernate.jpa.HibernatePersistenceProviderを設定する。
このクラスは、org.hibernate.ejb.HibernatePersistenceのJavadocに記載されている、代替となるクラスらしい。

#jjug_ccc #ccc_r15 JJUG CCC 2014 Fall 参加レポート / R1-5 JavaでやってみるThe Twelve-Factor App

※この投稿は、丸太式 Advent Calendar 2014の6日目です

昨日に引き続き、JJUG CCC 2014 Fallの参加レポートです。

概要

R1-5 JavaでやってみるThe Twelve-Factor App
by 渡辺 祐さん(株式会社ビズリーチ)
@nabedge

メモ

The Twelve Factor App

  • 論文(っぽいよみもの)
  • Herokuの元CEOが作った
  • “どんなWebアプリケーションであっても、だいたいこの方針で作ればいいんじゃねーの”というもの

ポートバインディングを通じてアプリケーションを公開せよ

  • 【自らポートを開いて】
    • 開発したアプリケーションの”依存ライブラリのTomcat/Jettyで”ポートを開く
  • main()メソッドで起動できるので、Run As -> Java ApplicationでTomcatがポートをバインドして起動できる
    • WTP Plugin, Sysdeo Tomcat Pluginはいらない
    • Tomcatのバージョンアップも、pom.xmlのバージョンを変えれば良い→普通のリリースフローで対応可能

廃棄容易性

  • 高速な起動はJavaではあきらめる
  • グレースフルなシャットダウン: Shutdown Hookを使う
    • Springなら@PreDestroyアノテーションをつけるとシャットダウン前に呼ばれる

ログをイベントストリームとして扱え

  • 「全てのログを標準出力に集める」
  • とりあえず標準出力に出しておけば、後から好きなところに流せるので。
  • 依存ライブラリのログ出力はslf4jのブリッジライブラリなどで集約する
  • GCログは・・・

設定はOS環境変数に格納せよ

  • 設定ファイルに本番DBのパスワードも格納してVCSに管理するの?
  • OSが違っても環境変数の設定はほとんど同じ
  • 気の利いたライブラリはOS環境変数からの設定値読み込みは対応している
  • 今時ならChef/Puppet/Ansibleで設定するでしょ

依存関係を明示的に宣言し分離せよ、ビルド/リリース/実行は厳密に分離せよ

ビルド

  • バイナリ間の依存関係管理機能のあるビルドツールを使う(Maven, Gradle)※Antはダメよ

  • ビルドの結果はjar, warというバイナリ

  • バイナリはパッケージリポジトリサーバにデプロイするもの
  • MavenもGradleも、依存関係の解決にはパッケージリポジトリを使う

リリース

  • パッケージリポジトリから開発したアプリのJARと依存ライブラリのJARを取得して、依存JARをコピーしてZIPに固めてSCPで送り、サーバー上でUNZIPするまで

実行

  • java -cp “jar群の展開dir/*” com.example.ExampleMainClass
  • 設定は環境変数に格納されているはず

アプリケーションをステートレスなプロセスとして実行せよ

  • 少なくとも、Stickyセッションを使うな→現実問題難しい
  • ステートレス:アプリケーションサーバにセッションを持たせない

#jjug_ccc #ccc_r13 JJUG CCC 2014 Fall 参加レポート / R1-3 実例Javaトラブルシューティング! 稼働中のシステムを立て直した半年間の軌跡

※この投稿は、丸太式 Advent Calendar 2014の5日目です

11月15日に、JJUG Cross Community Conference 2014 Fallが新宿で開催されました。
朝の基調講演から参加するつもりでしたが、残念ながら寝坊してしまったので途中からの参加となりました。
今日から何日かに分けて、セッションのメモをAdvent Calendarの記事として公開していきます。

概要

http://www.java-users.jp/?page_id=1292#R1-3
by 谷本 心さん@Acroquest Technology(株)
@cero_t

メモ

世の中のシステムは完璧か?
→最近リリースしたシステムは完璧か?

完璧ならざるシステムの例

処理が遅い
アクセスを裁けない
脆弱性
システムエラー
etc…

方針

  1. 検索処理の高速化
  2. 購入エラーの是正
  3. 大量アクセスを捌く
    ※Struts + Hibernate + JSP

最初にやったこと

→まずはSeleniumの自動試験を作る
直す過程で壊すことがないように
→CheckStyle / FindBugsをかけてリファクタ
可読性を向上させながら、何とかする

→ダメだった。
1. 300KLのコード、大半がデッドコード
1. 試験項目作成中にもエラーが頻発
1. その間にも本番システムでエラーが多発

方針転換

  • 問題のトリアージ(優先度の設定)
    「一番の問題は何か?」→「購入に失敗すること」(損失の本質)
  • 購入に失敗する理由→これらの改善こそ【顧客が求めること】
    • 購入時にシステムエラーが起きる
    • 検索が遅くて購入までたどり着けない
    • TV放送でアクセスが集中すると、システムがダウンしてしまう
  • どうやって問題を見つけるか(ソースコード上の問題より、現場の問題をつかむ必要がある)
    • エラーの発生頻度(HTTPステータス、アプリケーションエラー)
    • 性能の傾向(通常のレスポンス、アクセス増大時のレスポンス性能)
  • どうやって現場の問題をつかむのか
    ※稼働システムにブレークポイントはだめ

    • ログの収集(アクセスログ、アプリケーションログ、MySQLのスロークエリログ
    • 低負荷な解析ツールの適用(ENdoSnipe等)
  • ログ分析
    • ElasticSearch(ログの蓄積) ※リアルタイム検索・解析エンジン
    • Kibana(可視化) ログ検索、可視化
    • Fluentd(収集)
  • 典型的な問題の検出
    • ENdoSnipe OSSのトラブルシュートツール。メモリリークや同一SQLの発行回数など。動的なFindBugsのようなもの

Mission1 検索処理の高速化

  • 問題点
    • 検索処理は日常的に重い(10~30秒程度)
    • 2~3日間隔で非常に重くなる
  • 分析状況
    • 100件/秒のアクセスで、全てのレスポンスが5秒以上かかる
    • 夜間、ほぼアクセスがない場合でも10~20秒かかる
    • 謎のレスポンス劣化(最大3000秒!!!)
      • スロークエリログを見ると、同じ形になる→間違いなくDBに問題
      • なぜ線形に上がるのか?→SQLを見ると、全て同じストアドプロシージャ
  • 謎のレスポンス劣化原因について仮説を立てる
    ※トラブルシューティングでは、まず問題を確認し、仮説をいくつか立ててから検証・考察するべき

  • スロークエリのストアドプロシージャを精査

    • Temporary Tableに数万件のinsert
    • 明らかに無駄な処理が多数あり、削ったが改善されず
    • Temporary Tableはどうしても必要だったので、
      • Temporary Tableをon memory
      • Temporary Tableのメモリ割り当てを増やす
        →結果・・・Disk I/Oが激減、CPUのI/O waitが激減。スロークエリも改善

Mission2 購入エラーの是正

  • 問題の確認
    • 購入処理の途中でエラー
      • システム内要因(NPE等)
      • 外部システム要因(DB,決済等の応答無し、等)
    • お客様からのクレームで気づく
    • カード決済が絡むので、問題
  • アプローチ
    • 購入処理の開始時にロギング(ログをDBに記録)
    • 処理の途中、終了時にもロギング
    • ログを解析して、正常終了した処理と異常終了した処理を可視化
    • エラー原因ごとに発生件数を整理して、件数が多いエラーから順に対応
    • バグパッチを当てるたびに、エラー数が半減
    • 外部システム要因のエラー以外は対処完了

Mission3 大量アクセスを捌く

  • 問題
    • TV放送などで大量アクセスがあるとレスポンスが悪化してシステムダウン
    • ストアドプロシージャは対処したが、CPU使用量は上がるので。。。
  • アプローチ
    • 同時アクセス数制限の強化 APサーバごとに、検索処理の同自利楠と数が一定数を超えたら503
    • 検索結果のキャッシング
    • 同時検索数を制限 ※同一検索条件は待たせ、キャッシュから読み取る。違う検索条件では同時に検索できる仕組み
  • 構成の問題
    ボトルネックはDBなのに、なぜAPサーバをスケールアウトしているのか・・・
  • アプローチ
    TV放送時にもスケールアウトしない
    →システムダウンがなくなる
    →レスポンス改善
    ※closeされていないコネクションが残ってしまう場合があった

このシステムの今後

  • 全面的なデザインリニューアルと同時に処理をRESTful APIとして再実装
  • 購入処理の非同期化
    • 在庫確保とカード決済ができた時点でレスポンスを返す
    • 外部システム連携部は自動リトライ
  • ElasticSearch / Kibanaの改善
    • シングルノードで運用しているので、たまに死ぬ
    • 処理負荷が高すぎて過去ログを流し込めない

まとめ

  • 長期的トラブルシュートでは、ログの可視化は効果的
    • 現実に起きている問題がわかる
    • 改善効果がわかる
  • 下手にスケールアウトすると怖い

ExCellaを使ってJavaアプリケーションからExcel出力

※この投稿は、丸太式 Advent Calendar 2014の4日目です

ExCellaとは

JavaのプログラムからExcelファイルを操作するライブラリとしては、Apache POIが有名ですが、決まったテンプレートに値を設定して出力するような使い方をする場合に必要なコード量が多いと思います。
また、出力する項目は変えずに値を設定するセルをかえるだけでもプログラムを書き換える必要があり、とても扱いやすいとは言えません。
ExCellaは、そんな扱いづらいPOIのラッパーとして利用できるOSSのライブラリです。

ExCellaのコンポーネント

ExCellaは3つのコンポーネントで構成されており、それぞれのJarと、一緒にダウンロードできる依存ライブラリのJarをクラスパスに追加することで利用できます。
※現状Mavenで管理することはできませんが、GitHubのリポジトリを見る限りでは今後対応する方針のようです。
* ExCella Core
ExCellaのコアとなるコンポーネント。
他のコンポーネントから共通で利用される。
* ExCella Trans
データ移行の為のコンポーネント。
ExcelファイルやCSVファイルからSQLやJavaオブジェクトを生成できる。
* ExCella Reports
Excelの帳票を出力する際に用いるコンポーネント。
テンプレートに埋め込まれたタグを置換して、ファイルに出力できる。

Excel帳票出力のサンプルコード

ExCella Reportsを使った帳票出力のサンプルコードです。
単純にタグと値を一対一で置換する場合、これだけのコードで出力できます。

String templatePath; // テンプレートファイルのパス
String outputPath; // 出力先のファイルパス
 
// 1. ExCellaのReportBookオブジェクト(Excelのワークブックに相当)を生成する
ReportBook reportBook = new ReportBook( templatePath, outputPath, ExcelExporter.FORMAT_TYPE);
// 2. ExCellaのReportSheetオブジェクト(Excelのシートに相当)を生成する
ReportSheet reportSheet = new ReportSheet( "シート名");
// 3. テンプレートのタグを置換する値を設定する
// この例では、テンプレート中の ${tagName} を tagValue に置換します
reportSheet.addParam( SingleParamParser.DEFAULT_TAG, "tagName", "tagValue"); 
// 4. ReportBookにReportSheetを追加する
reportBook.addReportSheet( reportSheet);
// 5. 帳票出力処理を行う
ReportProcessor processor = new ReportProcessor();
processor.process( reportBook);

ExCella Reportsは単純な1対1の置換だけでなく、指定範囲の繰り返し置換にも対応しています。
詳しくは、公式のドキュメントをご覧ください。

最後に

ExCellaはOSSのプロダクトとしてはコミュニティがあまり活発ではありませんが、帳票文化が根付いている日本ではかなり便利に使えると思います。

参考リンク

Oracleのリストの最大数を考慮したHibernate Criteria

※この投稿は、丸太式 Advent Calendar 2014の2日目です

DBMSのOracleには、IN句等に渡せるリストの最大長に制限があることは皆さんご存じだと思います。
ご存じ無い方のために例を示しますと、以下の二つのSQLでは、前者は成功し、後者は失敗します。

SELECT * FROM AnyTable WHERE AnyColumn IN (1, 2, 3, (), 1000);
SELECT * FROM AnyTable WHERE AnyColumn IN (1, 2, 3, (), 1000, 1001);
-- →ORA-01795: リストに指定できる式の最大数は1000です。

そもそもこんなSQLが発行されているのが大きな間違いですが、保守開発などどうしても大きく変更できないときは、このようにしてお茶を濁すことになります。

SELECT *FROM AnyTable WHERE AnyColumn IN (0から1000) OR AnyColumn IN (1001以降);

単純ですね。IN句を1000要素毎に分割してORでつないだだけです。

さて、ここでJavaからHibernateのCriteriaを使う場合に、Collectionの要素が1000件を超えるとやはりエラーとなってしまいます。
OracleのDialectでは対応出来ていないため、Criteriaに条件を指定する際には以下のように工夫する必要があります。

Collection<T> theCollection; // 要素数が1000を超えるかもしれないコレクション
Criteria criteria = session.createCriteria(AnyTable.class);
// criteria.add(Restrictions.in("AnyColumn", theCollection)); //←これではtheCollection.size() > 1000の場合にエラー
List<T> tmpList = new ArrayList<>(theCollection);
Junction disjunction = Restrictions.disjunction();
while (tmpList.size() > 1000) {
  List<T> subList = tmpList.subList(0, 1000);
  disjunction.add(Restrictions.in("AnyColumn", subList));
  tmpList = tmpList.subList(1000, tmpList.size());
}
if (!tmpList.isEmpty()) {
  disjunction.add(Restrictions.in("AnyColumn", tmpList));
}
criteria.add(disjunction);

面倒ですね。
コレクションを渡してdisjunctionを返すユーティリティメソッドを作っておくと多少は楽になるかもしれません。

まとめ

こんな事をするくらいなら、はじめからサブクエリを使いましょう。

今年も無謀な挑戦を始めます[Advent Calendar 2014]

※この投稿は、丸太式 Advent Calendar 2014の1日目です

無謀な挑戦=一人でAdvent Calendarを埋める

無謀ですね。
海外ではクリスマスまでのカウントダウンのために、Advent Calendarというカレンダーがあるそうです。
近年、日本のIT系技術者の間ではあるテーマに基づい12月1日から25日まで、日替わりでブログ等のエントリを書く催しがはやっています。

目的

一人でアドベントカレンダーを埋めることで、単純にアウトプットを増やすだけでなく、継続してアウトプットするきっかけにしたいと考えています。
挫折したら挫折したで、また来年がんばろうとは思いますが、去年も挫折しているので今年は毎日何かしら書きたいです。

今年のアドベントカレンダーは、「丸太式 Advent Calendar 2014」にまとめているので、気が向いたらご覧ください。

※去年の挫折した結果はこちら→http://www.adventar.org/calendars/256

GitLabにPushしたときに”Could not find rake-10.3.2 in any of the sources”が発生した場合の対処

このバグ踏みましたorz
Could not find rake-10.3.2 in any of the sources · Issue #7230 · gitlabhq/gitlabhq

詳しくはチケットのほうを見ていただければわかると思いますが、
とりあえず解消するには

sudo apt-get purge ruby1.9.1 libruby1.9.1

を実行すればOKです。

apt-getで追加したrubyと共存したいときはどうすればいいんでしょうね・・・(あまり調べてない

HipChat+HubotでChatOps【とりあえずHipChatにHubot参加編】

久しぶりの投稿です。今週は珍しく成果が出たので、きちんとアウトプットします。

HipChat

HipChat(http://hipchat.com)とは、Atlassianが提供するグループチャットサービスです。
以前は無料で使えるのが最大で5人だったそうですが、現在は無料でもユーザ数無制限になっています。

Hubot

Hubot(https://github.com/github/hubot)は、GitHubが使っているBotフレームワークです。
Adapterを追加することで、様々なグループチャットやWebサービスと相互に接続することが出来ます。
Node.js上で動作し、AdapterやスクリプトはCoffeeScriptで記述できます。

heroku

Ruby, Java, Node.js等のPaaSのサービスです。
今回はHubotを動作させるために利用しました。Hubotを使う位であれば無料で利用できます。

ChatOps

チャットを使ってサービスを運用しようとする最近のはやりです。DevOpsのツールとの相性も良さそうです。
HipChatやCampfire等のグループチャットにHubot等のBotを接続して、Botを使ったオペレーションを行います。

・・・で、何したの?

今日はHubotの設定とHipChatに参加させただけです。(つまりほとんど何もしていない)
特にハマった点はないので、記事の最後に参考にしたリンクを載せるくらいにしておきます。

(Redmine|GitLab) + HipChat

ついでにRedmineとGitLabをHipChatに連携して、月次で処理しているスクリプトの処理結果をHipChatに表示するように設定しました。
Redmineはプラグインを追加しただけですし、GitLabに至っては標準機能として連携出来るので、こちらもリンクを載せておきます。

これからの目標

NginxのアクセスログとエラーログをFluentdで収集して簡単な解析をしてみようと思っています。
Hubotは解析のトリガーにするか、特定時点の解析結果を取得するのに使うか検討中です。

ところで

このブログを置かせてもらっているConoHaで、オブジェクトストレージ機能の提供が始まりましたが、1年間の無料モニターに当選しました。
オブジェクトストレージも触るのは初めてですが、アウトプットできる何かを作ろうと思います。


Photo By: i k oCC BY-NC-SA 2.0