Home > Archives > 2012年6月 Archive

2012年6月 Archive

HiveServerでファイルディスクリプタがリークする件について

@tagomorisさんがこんな事を言っていたので、調べてみました。

まずは、.pipeoutファイルについて。
結論から言うと、Hiveの不具合です。
((追記 2012/06/23 14:00)Hiveのプログラムの書き方は良くないんだけど、Leakはしていないと思われます.)

この.pipeoutファイルはSessionStateのstartメソッドでOpenされます。startメソッドはHiveServerの場合、クライアントがコネクションをはった時にコールされます。(HiveSourceCodeReadingの発表資料にもちょっとだけ書いてあります。)

.pipeoutファイルはHiveServerではpipeInという変数によって、BufferedReader -> FileReaderを通して参照されます。 ↓のような感じ。
pipeIn = new BufferedReader(new FileReader( .pipeoutファイル ))
後始末の処理として、pipeIn = null としています。これがNGです。closeしていません。 

GCによってcloseされないのか?と思うかもしれません。が、されません。
GCによってcloseされるのはFileInputStreamだけ(あとはFileOutputStream)です。
FileInputStreamはfinalizeでcloseする事を仕様として定めていますが、FileReaderは仕様として定めていません。FileReaderの親クラスであるInputStreamReader, Readerともに仕様にありません。
JDK6のソース(OracleJDK, OpenJDK)を見ても、finalize処理は実装されていません。
※ちょっと嘘書きました。末尾に追記しました。(FileReaderはコンストラクタでnew FileInputStreamしていますのでGCでcloseされます)

ちょっと横道にずれますが、.pipeoutファイルってdeleteOnExitしているんですよね。このファイルは毎回異なるファイル名で作成しているので、コネクションをはる度に解放されないオブジェクトが溜まっていきます(まぁメモリリークしているわけですが、定義的にメモリリークと言って良いのかわかんない)。 HiveServerのclean処理で.pipeoutファイルをdeleteしても、deleteOnExitのリスト(正確にはLinkedHashSet)からは消えないままです。

というか、openしたままdeleteできるかどうかってプラットフォーム依存だった気がしますが、もう直ってるんでしたっけ? BugID忘れた&調べる気ない(・x・)

次に.txtファイルについて。
これってLog4jで出力しているファイルの事なのかな?(デフォの設定だと、.logなので違うファイル?)
こっちはまだ調べていませんが、Log4jのFileAppenderの場合、ファイル名を設定する度に1つ前に開いていたFile(というかWriter)をcloseする処理が入っているところまでは確認済み。HiveのExevDriverで以下のように書いていて、これってFileAppenderのreset処理呼ばれるっけ・・。と疑問に思ったところで、こっちはいったん調査終了。

    System.setProperty(HiveConf.ConfVars.HIVEQUERYID.toString(), HiveConf.getVar(conf,
        HiveConf.ConfVars.HIVEQUERYID));
    LogManager.resetConfiguration();
    PropertyConfigurator.configure(hive_l4j);

たぶんこっちの事かな・・。 hive_job_log_(sessionId)_(乱数).txt
こっちの場合は、HiveHistoryというクラスが担当で、PrintWriterを介してファイルをOpenしており、close処理がfinalizeで呼ばれています。このHiveHistoryはSessionStateのstartメソッドでインスタンス化されます。

SessionStateはThreadLocalに保存されます。HiveHistoryはSessionStateが保持しています。HiveHistoryは様々な場所から参照されていますが、他のどこにも保持はしていません(インスタンス生成時にSessionStateを受け取っているので、一見循環参照か!? と思ったけど保持はしていないです。)。Thriftがどういうモデルなのか分からないですが、仮に接続毎にスレッドを生成しているのであれば、接続がきれた時にSessionStateがThreadLocalから消えて他のどこからも参照がない場合(これはまだ確認してない)、SessionStateはGCの対象になります。その時にHiveHistoryへの参照が全て無くなるので、GC対象となるはずです。

仮定が多くて調査になっていないですね(=w=;)
jmapとjhatでどういう状態になっているか確認すれば良いだけなのですけどね。

お腹すいたのでいったん調査終了(・ω・)

※(追記)
当たり前の事なのでわざわざ書いてませんでしたが、一応捕捉しておきます。
(少なくともJavaでは)openしたものはきちんとcloseすることを明示すべきですし、
別にGCまかせにすることを推奨とも良しともしていないです。(そんな事一言も書いてないですよね(>ω<))
原因となりうるかと、それを良しとするかは軸が違うと思うのです。

(さらに追記)

FindBugsで解析をすると分かるらしいです。
今度試してみます。

ふむふむ。tagomorisさんのtweetによると、CDH3u2との事なので丁度ぶちあたってしまうのですね。
あと、確認したソースのバージョンを明記してなかったです。trunkとCDHu4で見てました。

(追記)2012/06/23 13:00

FileReader(File)はコンストラクタでsuper(new FileInputStream(file)) としていました。
という事で、GC走ればcloseされます。 
簡単な確認テスト: https://gist.github.com/2976972 

とあるサービスの技術資料まとめ の目次

せっかくなのでとあるサービスの技術的な事をまとめていこうかなと思ってます。
(作り直しているはずだし、消えるものなら片鱗を残しておこうかなと)

抜け漏れがまだまだあるので、思い出したら随時こっそりと更新していきます。画像も多くなるし、BlogじゃなくてGoogleDocとかのが良いのかな(自分のWikiにまとめる事にしました)。こういう内容は書かないの?とか、このサービス使って書くと良いよとかあったら教えてください。お願いします。
基本的にまったり進行。公開が遅かったらお尻をペシペシと叩いてください。

部署やプロジェクトを兼任していたので、やった事はこれ以外にも沢山ありますが、そのうちの1つとして記録を残します。併せて、なぜそうしたのかという所も覚えている範囲で書いていきます。また、数年前に開発が始まったものですので、今だったらこうするなという事も。
ぼかして書くので抽象的になるかも。
こういう事を去年のCEDECに公募してみようと思ったのですが、作業の遅い自分は間に合いませんでした。

なお、以下の内容はほぼ一人でやった事になります(´;ω;`)ウッ・・

書かないこと

  • 数字に関するもの
  • 内部的な作業フローなど
  • 日々の運用作業について
  • 課金やロビーサーバ、ゲームサーバ、パッチなどの話
    Webから外れるところもばんばん書くことはできますが、このBlogを見ている人はMMOGに興味なさそうだし。興味があれば直接会ったときにでも聞いてください。(言えない事は話しませんけど)
  • 所謂ビジネスロジックと言われるところ
  • 毒・悪口

目次

  • 開発環境について
    • プログラマとプログラマ以外の作業環境
    • レポジトリについて
    • 構成図
    • 継続的インテグレーションについて
    • 開発系のツールについて
    • 通知の話
  • 環境の種類
    • 所謂ステージングとかプロダクション環境とか。全部で5種類。
    • データの共有など。
  • サーバ構成について
  • ミドルウェアについて
    • ミドルウェア一覧と採用した理由など
  • コンポーネントについて
    • HTTPD, APS, Cache, KVS, RPC, Scheduler, MQ, Solr, RDB, API, Watch, ...
  • アプリケーションについて
    • 設計について
    • フレームワークについて
      • どういう構成になっていて、どういうフローで実行されていくか。
    • 機能
      • フレームワークとしての機能
        • 各種ユーティリティ系クラス
        • 多言語、共通基盤、
        • その他
          • BBCodeの実装
          • キーワードハイライタ
      • アプリケーションの機能
        • アカウント(ID/課金、キャラクタ周り)
        • マイページ、通知、日記、グループ機能、フォーラム、画像、イイネ、タグ、フォロー、アクティビティ、プレイログ、検索(4種類)、ランキング
        • バナーやメニュー制御については書かない
      • ニュース・トピックスの話
      • ゲーム連動
      • デバッグの機能
    • テンプレートエンジン
      • 機能の紹介
      • 2,30個以上はあるので他所のエンジンにないものを重点的に。
  • キャラクタのアバター画像について
    • 仕組みについて
    • 夏にやった企画について
  • 検索について
    • 構成の話
    • 拡張した機能について
    • その他
  • i18N対応について
    • ロケールについて
    • リージョンについて
    • タイムゾーンについて
    • アプリ本体
      • 3(4)リージョン
      • 5(6,7)言語
      • Viewレイヤーの話
    • ツール
      • テキスト管理のツールについて
      • 変換ツール
  • JavaScriptについて
  • CDNについて
  • ログについて
    • ログの種類
    • トレースログについて
    • ユーザーログについて
    • その他
  • ログの解析について
  • 監視について
    • 監視の仕組み
    • 通知について
  • RPCサーバについて
    • プロジェクト間共有の仕様でトランスポート層とかの仕様まできっちり決めてあるが、細かい事は言えないので概要。
    • プロトコルの概要
    • 実装について
  • APIサーバについて
  • IRCDの活用について
  • 管理ツールについて
    • 運用をするためのツール
    • モデレーション
  • 運用ツールについて
    • FTPを使わない話
    • NFSを使わない話
    • 作ったツールの話
  • デプロイの話
  • メンテナンスについて
  • 各種障害の内容について(書ける範囲で)

むかしむかし、とある会社で3日で作ったツールについて

むかしむかし、とある会社で3日で作ったツールについてのお話。

昔の社内勉強会用の発表資料が出てきたので、せっかくなので晒してみようと思います。(健全な男子が喜ぶ系のシステムの資料も出てきたのですが、これはまたの機会に・・・)
3年くらい前のもの(ソースみたら2009年でした)なので、そのあたりを考慮していただけると助かります。

なお、Web系の企業では動画とか全然だと思いますが、コンシューマ系の開発だと動画でやりとりできると便利ですよね。

さすがに資料をそのまま載せるわけにはいかないので、所々塗りつぶして、末尾にページを何枚か追加しました。
そういえば、CEDECである人が発表でちょこっと触れたみたいで、4gamerにもスライドが載っていたのですが、名前伏せられてるし(ツール名にプロジェクト名が入っていたので広報チェックでNGになったんだと思います)、モザイクかかってるしでちょっと悲しかった記憶を思い出しました。

(追記 2012/06/28 10:00)
・資料中のRAIDの説明がなんかおかしいです(RAID""二重化)。二重化+パリティのRAIDでNetAppだったらおそらくRAID4の事だと思いますが、詳細は私にはわかりません。
・資料の31ページが見れないという事でしたが、今見たら見れるようになってました。うーん・・?

ツールの概要を一言で言うと、ニコニコ動画のぱくりです。
プロトタイプを1日で、その後2日で作り直したものです。その後細かい修正はちょこちょこ入れてましたが、基本的には放置運用でした。(ごめんなさい・・)

本来の業務を放り出して、これのために3日みっちり作業しました。
一番時間がかかったのは、デザインをニコニコ動画っぽくする事とメンテ画面のトロがナイスボートって言ってる画像を探す時間です。単体テストのコードがほとんどありませんし、正直ソースコードはかなりひどいです。
いくつかのプロジェクトから、欲しいクレクレと来ましたがソースコードを見て絶望したと思います。

作った理由は、スライドにもありますが、
・単純にそこらへんに動画を置かれるとファイルサーバが死んでしまう事
それと、
・ローカルで動画をエンコードするとその間他の作業が出来なくなってしまう
・その動画、他の人全員が見れる形式とは限らない&codecによってはライセンスの購入が必要
という事です。

当時の心境を思い出すと、現実逃避も含まれていたと思います。(煮詰まっているときに他の作業すると楽しいですよね:p)

データセンターに置かなかった理由は、
・ネットワーク帯域がやばい事になる
・データセンターに置く機材の話とか発注とか予算とか
それを正攻法で解決しようとすると、いろんな人といろんな調整をしないといけなくなり、それには膨大な時間がかかります。(これは組織の問題かもしれませんけどね)
余ってるPCなら稟議とか要らないし、ソフマップでHDD買えば普通に経費で落とせるし。
※こんなやり方、真似しちゃダメですよ。いろんな人のヘイトも上がるし、後々面倒な事になります。(当時の状況として大人の事情ってやつがいっぱいありまして.. アルヨネ?そーいうこと。)

MP4をコンテナにしたのは、その当時FlashでMP4/H264を再生できるようになったからです。みんなが見れる環境(つまりブラウザで再生できるもの)であれば何でも良かったのですが、H264画質がすばらしいですし。(画質にうるさい人が多いというわけではなく、画質が悪いと動画中の文字がつぶれてしまう)

一番問い合わせが多かったのが動画がエンコードできないというもの。
みんなFrapsで撮影しているものかとてっきり・・。

アップロード部分をFlashにしているのは、アップロードの進捗状況を分かりやすくするため(アップするファイルが未圧縮なので巨大ですし)と、ブラウザのアップロード2GBの制限(これはブラウザによりけり)を避けるためです。
それなのに、Apacheのビルドをミスって2GB以上アップロードするとContentTypeがオーバーフローして、アップロード出来ない状態が残念です・・。(ajp_proxyもtomcatも確認しましたが、apacheのレイヤーでNGになってました。)

というわけで、いくつか画面をキャプチャしました。私の家で動かしたものになりますので、サンプル動画として私のコレクションをアップしています。当たり前の事ですが会社でこういう動画を共有していたわけではありません。(と、当たり前の事を書いておかないと時々勘違いをした人が騒いだりするので)

TOPページ。
社内(プロジェクト)共通のLDAPがあったので、それを使ってユーザー識別してました。

アップロード画面。
アップロードするファイルのサイズが大きいので、簡単なプログレスを付けてあります。解像度は縮小無し~HD、SD等のサイズを選択できます。有効期限は無制限、1年などなど。これはエンコードした動画を削除する期限ではなく、オリジナルの期限です。基本的にエンコードした動画は削除しない方針でした。

アップロード完了
すると、こんな感じの画面になります。

アップ後
動画をアップすると、キューもどきに入ってサムネイル生成待ちの状態になります。

サムネイル生成
エンコードサーバで動いているデーモンがキューもどきを見ていて、最初にサムネイルを作ります。マウスカーソルを合わせるとパラパラと表示されます。海外の動画サイトに良くあるアレです。

エンコード
サムネイルの生成が終わったら動画のエンコードを開始します。
エンコードの状況をヘッダで表示できます。今どれくらいまで変換しているのか確認する事ができます。
なお、失敗しても(しなくても)リテイクしたい場合はフラグを立てるだけです。

動画の再生ページ。
全画面表示とかもできます。右上のアイコンからメタ情報の編集とか、ダウンロード(MP4 or オリジナル)ができます。

動画の編集画面。
まぁだからなんだって話ですが。。。スライドにも書きましたが、ブラケットで括るとタグとして認識します。

検索画面。
タグを押すとこの画面に飛びます。画像だと1件しかなくて見栄えがあれですね。

簡単なログ画面。
何が起こっているのか知りたい人向け。

RSSは、サイト全体のもの、ユーザー毎や動画毎にあるので、自分が投稿した動画にコメントがついたみたいなものが取れます(たぶん、つけた気がする)

何かを導入したり、使って欲しいものがある場合、一番手っ取り早い方法は本来の趣旨を主張するのではなく、メリットを主張するのが良いと思っています。例えば、Subversionのようなものを使って欲しい場合に、リソースを集中管理して履歴が追えて~とかじゃなくて、レポジトリに入れる事で裏で自動でリソースを変換します、HTMLファイルだったら文法チェックしてくれます、誰が何をやっているのかわかるようになります。とか。このツールもHDDが~とかじゃなくて、ローカルで変換しなくてもアップロードするだけで、あとは良い感じに全部やってくれます。とかとか。
まぁ管理者の幸せとユーザーの幸せはまったく異なる事がほとんどですので、それを押しつけても説得なんてできませんよね。

ツールを作るときは、他のシステムとも連携できるようにAPIを用意したり、何かのプロダクトにべったり依存しないようにするのが理想だと思いますが、当時の私はまだまだ未熟でそこまで気配り出来ませんでした。今なら似たようなプロダクトがあると思うのでそれを使うのが良いと思います。

世の中、他社には面白いツールがいっぱいあるんだろうなぁとイロイロ気になります。
こっそり教えてください(・ω・)

GrowthForecastが便利な件について

定期的に処理を実行するものがあって、その結果や中間結果を可視化したいなーと思うときがあるわけです。表示したい値は簡単に出せますが、それを見やすい形で出すのってけっこう面倒なものです。ログファイルをgrepしろやーはちょっと嫌です。グラフ表示する場合はCactiあたりが有名だと思いますが、グラフの追加に手間がかかるのがツライのです。とりあえずサクッと表示したいのです。

噂によるとkazeburoさんのGrowthForecastというものが便利という事なのでさっそく試してみました。

今回cpanmも初めて入れました。これも便利です。

まずはcpanmのインストール。Fedora17くらいだとyumにあります。CentOSだと無かったので手動で入れます。

cd ~/
mkdir bin && cd bin
curl -LOk http://xrl.us/cpanm
chmod +x cpanm

環境変数の設定。みんなのBLOGを見ると~/perl5を指定するのがお作法みたいですが、home直下にlsで見えるディレクトリが増えるの嫌だったので適当に変更。

export PERL_CPANM_OPT="--local-lib=/home/libido/.perl5_extlib"
export PERL5LIB=/home/libido/.perl5_extlib/lib/perl5:$PERL5LIB
export PATH="/home/libido/.perl5_extlib/bin:$PATH";

RRDToolのインストール

wget http://search.cpan.org/CPAN/authors/id/G/GF/GFUJI/Alien-RRDtool-0.03.tar.gz
cpanm Alien-RRDtool-0.03.tar.gz

GrowthForecastのインストール

git clone git://github.com/kazeburo/GrowthForecast.git
cd GrowthForecast
cpanm --installdeps .

GrowthForecastの起動

# ForeGroundで起動
perl ./growthforecast.pl
# BackGroundで起動する場合はこんな感じ?
perl ./growthforecast.pl > /home/libido/log/growthforecast/gf.log 2>&1 &

後は以下のようにHTTPのPOSTで値を送りつけます。実際はURLのservice_nameとsection_nameとgraph_nameの部分をそれっぽい名前で指定してあげます。
これだけです。
事前にグラフの設定とかは要りません。POSTすると勝手に作ってくれます。
パラメータのmodeは、count, gauge(省略するとこれになる), modifiedを指定できます。numberが送りつける実際の値です。

$ curl -X POST -F number=10 -F mode=count "http://tamsvr03:5125/api/service_name/section_name/graph_name"
{
  "data": {
    "adjust": "*",
    "adjustval": "1",
    "color": "#9933cc",
    "created_at": "2012/06/05 17:06:10",
    "description": "",
    "gmode": "gauge",
    "graph_name": "graph_name",
    "id": 31,
    "llimit": -1000000000,
    "md5": "c16a5320fa475530d9583c34fd356ef5",
    "meta": "",
    "mode": "count",
    "number": 10,
    "section_name": "section_name",
    "service_name": "service_name",
    "sllimit": -100000,
    "sort": 0,
    "stype": "AREA",
    "sulimit": 100000,
    "type": "AREA",
    "ulimit": 1000000000,
    "unit": "",
    "updated_at": "2012/06/05 17:06:10"
  },
  "error": 0
}

これでブラウザから見えるようになります。↓こんな感じ。クローラーのログを可視化してみました。一部ぼかしてあります。

綺麗な構成としては、ログファイルをFluentで~みたいになるのかもしれませんが、今回はプログラムが小さかったのでクローラーが定期的にGrowthForecastに直接投げるようにしました。

APIの注意点としては、numberは整数じゃないとダメです。floatはNG。
ちなみに、時間を指定(後から集計して○時のデータはこれだぜーみたいな)してグラフを描画したい場合は、HRForecast というものがあって、そっちを使うと良いみたいです。

インストールも、使い方も簡単なので、とりあえずさくっと見えるようにする場合にすごく使えるツールです。

参考にしたページ
http://blog.nomadscafe.jp/2011/12/growthforecast.html
http://d.hatena.ne.jp/oranie/20120517/1337225732
http://www.omakase.org/perl/cpanm.html

RubyのURIがホスト名にアンダースコアがあるとparseできない件

Ruby(1.9.3)のURI.parseはホスト名にアンダースコアが入っているとURI::InvalidURIErrorが発生します。

require 'uri'
p URI.parse('http://hoge_fuga.example.com/page?id=xxx#yyy')
`initialize': the scheme http does not accept registry part: hoge_fuga.example.com (or bad hostname?) (URI::InvalidURIError)

仕様として、URIについては、RFC-2396とRFC-3986, IRIとしてRFC-3987があります。
アンダースコアはRFC-2396ではNGだったけど、RFC-3986ではOKになっているのですが、どうやらRubyのURIライブラリの対応が追いついていない(?)ようです。ちょっとRuby界隈には疎いのでどういう状況なのかは正確には把握していません。

代替案を探していたら、addressableというライブラリが対応しているという事なので、さっそく使ってみました。

さくっとgemでインストールします。addressableのバージョンは2.2.8でした。

gem install addressable

簡単な確認。

require "addressable/uri"
uri = Addressable::URI.parse("http://hoge_fuga.example.com/page?id=xxx#yyy")
p uri.host
p uri.port
p uri.request_uri
"hoge_fuga.example.com"
nil
"/page?id=xxx"

パースできました。
ただ、標準のURIだと上記の場合でもportは80を返してくれるのですが、addressableは返してくれないみたいです。しょんぼり。

参考:
http://d.hatena.ne.jp/kitamomonga/20100316/ruby_gem_addressable_howto

 

Home > Archives > 2012年6月 Archive

Search
Feeds

Return to page top