Home > 日記 > 日記2009前期 Archive

日記2009前期 Archive

メンテ画面はナイスボート!

例の動画サイトですが、今回は更新内容が多かったのでメンテ画面にしました。(内製アプリの話です)

さて、味気ないページを出すのも気が引けるのでここは何か面白い仕掛けをしたいところ。

というわけで週末にこしらえたのです。画像を探すのに1時間くらいかかりました。

メンテモード(RedirectMatchでメンテページに飛ばす)にするとナイスボートが表示されます。動画じゃなくてトロステーションのやつですが。

見た人が「おやっ?」と思ってくれれば良いのですが、メンテモードにした10秒後くらいに突っ込みが入りました(≧∇≦)b

私が作るアプリには毎回イースターエッグを仕込んでいるのですが、今回は仕込んでいません。というのもなかなか良いネタがなくてデスね・・・。ネタ募集です。

さて粗方機能の追加をしたので、そろそろVERUPは打ち止めの予感。楽しみが1つ減ってしまった(´・ω・`)

疲れている理由

ほとんどが、エー・・・・と思って精神的にどっと疲れるわけです。
最近困っている事を書いていきます。

 

私「タスクがキツキツだったら言ってください。逆に余裕で暇な場合も言ってください。」
「え?暇な場合も言わないと行けないんですか?」

 

私「このSQLの実行計画確認しましたか?」
「実行計画ってなんですか?」
(DBがSQLを内部でどういう戦略で処理していくかというものを軽く説明する)
何回言ってもコスト=速い。重いではないという事を理解してくれない。

 

私「インデックスとかきちんと貼りましたか?」
「インデックスって何ですか?」
・・・・今までどうやって作ってたんだ。

 

私「ここがトランザクション境界になるんだけど~」
「トランザクションってなんですか?」
・・・・今までどうやって整合性保っていたんだ。

 

私「私会議に出られないので代わりに聞いておいてください」
「はいわかりましたー」
・・・・
私「どんな話がありました?」
「特に何も無かったです」
(アジェンダにはけっこうな量の議題挙がってたんだからそんなわけあるかい)
私「本当に何も無かったですか?」
「はぁ。関係なさそうなんで聞き飛ばしました」
・・・関係あるかないかを判断できるほど業務知識を吸収していないダロウ。。結局他の人に聞きました。

 

 

タスクが止まらないようにこういう風に進めてこういう設計をすれば、止まることもないし問題も分離できますよという道を示しているのに、そうしないで困りましたと言う。
(そうならないように道を示したじゃん・・・)

 

「今この機能作っていてエラーが出るんです。良くわからないんですけど動いたんでここ(共通部分)修正していいですか?」
「どんなエラーが出るの?」
「画面になんかばーっとエラーが出るんです。修正していいですか?」
「いや、だからどんなエラーが出てるの?」
「なんちゃらExceptionっていうのが出るんです。でここ修正すると動くんで直していいですか?」
「いや、なんちゃらExceptionって言われてもなんちゃらの部分が重要なんじゃないの? 私これから会議なのでIRCにスタックトレース貼っておいてください」
「私の画面に今出ています」
「いや、だからこれから会議なので貼っておいてください」
・・・・ 会議 終わって ・・・
「貼りました」
貼ってあったものはスタックトレースの一番上のメッセージのみ。
「全部貼ってください」
・・・・
この修正入れたら他の全部の部分が動かなくなるんですが・・・。
「この修正入れたら他の部分動かなくなるんですが、影響範囲はきちんと調べましたか?」
「よくわからないですけど、動いたからいいかなーって。」
「よく分からないのに修正しちゃうんですか?」
「まぁ動いたからいいかなーって」
「いや、だから他の部分が動かなくなるじゃないですか。きちんと影響範囲って調べてますか?」

 

「名前が結果に入っていないんですけど、これってこのテーブルに名前を格納していないからですよね?」
私「ぬ?」
「名前帰ってこないんですよ。で、このテーブルって名前を持って居ないじゃないですか」
私「持っていないですよ。他のテーブルにあるので」
「で、名前帰ってこないのでここが作れないんですよ」
私「帰ってきますよ。帰ってくるように作っているので」
「帰ってこないんですよ。」
私「それはあなたが作ったテストデータの問題ですよね? 本当に帰ってこないんですか?全部」
「はい」
私「本当に確認しましたか?」
ホワイトボードで1から説明。

 

私「これってどのマイルストーンで実装する予定なんですか?」
「さぁ・・」
私「じゃぁプランナーに確認しないといけないよね?」
「たぶん今回は入れないんだと思います」
私「たぶんって、それはプランナーさんに確認したの?」
「いえ。確認していないです」
私「確認してください」
・・・
「確認してきました。今回はやらないみたいです」
私「じゃぁいつやるの?」
「さぁ・・・」
私「毎回言ってるけど、いつやるのか聞かないと予定立てられないよね?」
「はぁ・・」
これは本当に毎回言ってるんです・・・。今回やらないならいつやるんだって所まで聞くように言ってるのに。。

 

この前は、とある機能を使って1つのコンテンツを作っていたのに、そのデータ構造を把握していなかった事に驚愕しました。
これもホワイトボードで1から説明。

こんな感じの事がほぼ毎日。正直心に余裕が無くなっていきます。

 

1つの機能をいきなり任せるのではなく段階的に難易度を上げていったりしてるんだけど、普通にセキュリティホールがあったり大量のデータを考慮していなかったりと今のままだととてもじゃないけど任せられない。
フェルミ推定をするしかなくて根拠が無い場合や、そのプランだけでなくBプラン、Cプランもあるならいいんですが、「何となくいいかなー」でAプランのみで決められるとそれって考えていないよねって思います。

最近は自分で考えさせるために「どうしたいですか?」って返すようにしています。
ただ、何に困っていてどんな問題を認識していてどんな選択肢を用意したのかを聞くようにしているのですが、選択肢で迷っているならフォローできるんですが結局は私が選択肢を2,3個提示している事がほとんどで、それって自分できちんと考えるという事をしていないんじゃないかと思うわけです。
「私は前提を知らないし、そもそもどうしたいかを知らないので適当な事を言いますが、こうこうこういう事なんじゃないんですかねぇ。その場合は私はこう考えるのでこういう方法かもしくはこういう事に比重を置きたいならああいう手段をとりますけど、どうなんでしょうねぇ?」って言うと「それにします」。別にいいけど私は前提知らなくて適当に言ってるのにそれで本当にいいのか?と思います。

毎回ホワイトボートで1から10の事を書いて説明するのが疲れる。。
世の中には1言って10覚える人と、15まで言わないと10覚えない人がいるわけですが、1つの事象を全ての事象に単純に当てはめている感じを受けます。前提があってその結果があるわけで、この場合はこういう事だからYYYはダメって説明をしても前提がん無視でYYYはダメって覚えるみたい。

あと、内製フレームワークならともかくオープンソースプロダクト使っているんだし、本だって沢山あるんだし、検索すれば沢山出てくるんですよ。そのためにオープンソース使っているって最初に言ってあるんだし。

 

ある程度は教育係になっている上長に教育して欲しいもんだ。
前のプロジェクトで放置していたのなら、なおさら教育すべきだろう。
そのしわ寄せが私の所に来ても困るんですよ。正直負荷一向に減らないどころか増えてますよ。

 

一回突き放した方がいいのかな。
ユーザーには申し訳ないけど、障害を起こしてサービスが止まってそれを自分の力で解決するという事をしないと成長しないんじゃないかと思う。
自由にやる事と無責任にやるという事は全く違うわけで。

そういう意味で言うと、ほぼ毎日のようにツールの不具合を対応している人が居るんだけど、あれのせいで結構成長しているんじゃないかと思う(知らないけど)

 

みなみけ6

みなみけ 6 (ヤングマガジンコミックス)

買いました。
あれっ何故か内容を知っているぞと思ったら、テレビでやってた内容と同じでした。

 

ニコニコ動画でSAってアニメを見たんです。
色々とデッサン崩れてない?(;´Д`)

OJT研修

ワークショップ形式のOJT研修に参加しました。

本来はまる1日かけてやったものらしいので2時間でやるには無理があった気がしますが、それでも有意義でした。そして純粋に楽しかったです。

こういうのは明確な答えを見つけるのではなくて、考え方のバリエーションが増えたり、気がついていなかった事を気がつけるきっかけだと思うので、2時間でもきちんと収穫がありました。じゃぁ言葉で説明できるのかと言われると、それはそれで難しいのですが。

ワークショップなので他の人とその内容について話し合ったりもするのですが、人の考えって本当に十人十色です。すごくあたり前の事なのですが・・。そういう考え方もあるんだー!

コンセプト

ダメな企画の例

「機能や仕様などばかりを先に詰めて肝心なコンセプトが固まっていない・・・」

あー・・あるある(´・ω・)

 

コンセプトの役割

・企画の意味づけ
・企画の方針の決定
・スタッフ間の意思統一を図る

 

引用元サイト
http://ameblo.jp/kury999/entry-10240978149.html

 

サーバーどっかーん。

なんかSambaに接続できなくなってdevice busyになってしまいました。

とりあえずサーバを再起動。

起動せず。。。(;´Д`)

ディスプレイを引っ張ってきて繋げてみる。

invalid KERNEL operation。udevなんちゃら(ry

うーん・・・。BIOSをポチポチ・・。

ビービービービー!

その後、BIOSすら起動せず(´・ω・`)

 

CMOSクリアしたら全部治りました。

とりあえずSubversionのデータだけは退避しておこう・・・(´д⊂)‥ハゥ

 

動画共有サイト リリース

先日ぱぱっと作った動画共有サイトがリーダー会で報告されて正式運用に入りました。
こんなんで満足して貰えるならバンバン作るようヽ( `・ω・´)ノ
リーダー会といえば、ファイルサーバの話が挙がったようですが、もう値段言っちゃえばいいのにと思います。「ヨドバシで買ってる訳じゃないんだよ!NetAppなんだよ!」と。価格コムの値段から100倍~150倍してRAID分の係数をかければいいんです。そうすればちょっとは節約すると思います。まぁファイラーってどこの按分に計上されているのか知りませんが。

それはさておき、金曜日に帰宅する前にメールが来ていたのですがcinepakコーデックの動画をアップするとダメぽと言われました。バックエンドではffmpegを使っています。ffmpegはリンクしたライブラリのみで頑張るのですが、cinepakはエンコードのみでデコードは対応していません。というわけでavisynthをかましてDirectShowSourceとして扱うようにすれば対応できるのではないかと思いました。

まだ実験してませんけどね。この記事書いたら実験します。

(ここから追記)
実験成功! アプリの拡張が必要だけど1時間もかからないはず。
(ここまで追記)

それはそうと、ちょこちょこ弄っていると結構バグがあるなぁと・・・。こんな品質のものをリリースした自分が恥ずかしい。修正はしたので明日の朝一で更新しよう。ばれる前に・・・!
ついでにちょっと便利なRSSも入れてみた(自分がアップした動画についたコメント一覧とか、任意の検索結果に対するRSSとか)

 

地味にショックだったのが、「これテンプレートで画面弄れないかなぁ?」とデザイナの偉い人に言われたことです。デザインはご不満のようでした。
デザイン結構頑張ったんですけどねw
弊社のデザイナのクォリティについていけるわけがない!!。゚(゚´Д`゚)゚。

 

ヒョォォオッー!!

 what? what? HAHAHA!!!  →  ヒョォォォー!!

人生楽しそうでいいなぁ

本が届きました

例の本が届きました。

読んでみました。

・・・

・・・

ジャンルが云々とかじゃなくて、漫画として普通に面白くない(-。-)

そんなわけで仕事帰りに本屋によりました。ファイブの最新刊を買っていなかったので。

で、目についた本がこれ。

少年メイド 2 (B’s LOG Comics)

最新刊出てた。漫画として普通に面白いので購入しました。なんか色々と誤解されそうですが、そういう趣味はないです(´∀`)

ファイブの最新刊も残り1冊でしたが買えました。

 

明日はブリーチの最新刊が出るんだっけ(・´ω・`*)

Final Fantasy XIV

北米で行われているE3。
そのSCEカンファレンスの中でFF14が発表されました。( ゚Д゚ノノ"☆パチパチパチパチ (白々しいとか言うな!!)

 

ff14.png

 

次世代MMO「Rapture」の正体がナンバリングタイトルだったわけですね。オンラインです!!

日本時間の深夜あたり(?)にスクエニカンファレンスがあってその中でもうちょっと詳細な事を言うみたいです。

久しぶりにわくわくしました。(いろいろな意味で)

スクエニ・・・!

女装少年アンソロジー (ガンガンコミックスアンソロジー)

「女装少年アンソロジー」って漫画なんです。

出版社を見ると、スクウェア・エニックス

・・・

・・・・・

Σ【*゚д゚*】ぇっ

さらに公式サイトを見てみると、、、

え!アクセスランキング1位!?
ままま、まぢですか(((( ;゚д゚)))

思わずぽちってしまったじゃないか(;´Д`)

4週間待ち。FFCCのサントラと一緒に買ったせいでサントラも4週間待ちなわけですよ。
(1週間くらい後に分離されてCDだけ送られてきましたけど)

まぁ悲劇はここから始まるわけでして。

私のamazonのお勧めリストがこの一冊のためにいっきに腐女子モードに突入です。

前までは普通のコミックや技術書がぎっしりと並んで居たんですよ。確かにamazonのレコメンドエンジンは正確しすぎてマンネリ化してくるんですよね。意外性が無くなってくると言うかカオス理論が入っていないというか。で、別に1冊くらい変なの買ったからって影響ないと思っていたんです。

それなのに、上位は↓こんなんばっかり。゚(゚´Д`゚)゚。

少年メイド 1 (B’s LOG Comics)   絶対☆アイドル (角川コミックス ドラゴンJr. 133-1)   フダンシズム-腐男子主義- 1 (ヤングガンガンコミックス)

なんという破壊力(´・ω・)

ちなみに「少年メイド」は作者の趣味が大爆発しているだけで、ふつーの健全な漫画で結構面白かったです。

餅は餅屋に

うん。専門的な事は専門家に任せた方が良いと思いました。

餅は餅屋に(´・ω・)

 

とか言ったら「そば屋のカレーが美味しい事もある」と言われました。

(*・ε・*)

すんドめ

amazonで本やサントラを20数点ほど買いました(ノ∀`)

すんドめがDVDにもなっていて面白そうだったので全巻購入してみた。面白い(・∀・)

以下amazonのレビューより転載。

転校生の女の子は、カワイイ女の子。席はオタク系クラブに所属する、影の薄い主人公の隣に。ここまではベタですが、この子がクラブに入部し、冴えない主人公の側を付きまとう。主人公の心の中が、まるですべて分かっているかのような言葉で彼を操り、虜にしていく。

6巻まで読みましたが、鼻血とお風呂場でのアザになる速度から推測するに、この女の子は白血病か何かなんでしょうね。(実際に白血病だったら鼻血よりも歯茎とかから血が出るんだと思いますが・・)。6巻まで読むと二人の中が深まっていってるだけに、こういう描写を見るといろいろ推測して切なくなっちゃう(´・ω・)

↑なんか6巻が小さい絵が無くて商品追加できなかった。くるくるウィジェットって商品少ないと見栄えが良くないね

ノベリティ貰った(・∀・)

仕事頑張ってるからという事で貰いました(・∀・)

左は4枚のカードと、右は500円のWEBマネー。

大事に保管しとこ。

 

すごい雨だった

なんか天気がすごかった。。朝から雷鳴っていたし。雨はどしゃ降りだったし。

木曜日は7時頃に出社。夜の2時頃に寝ようかなと思ったら家のMySQLが止まってまして、リカバリーしてたら寝れなくなってそのまま出社パターンです。寝ると絶対遅刻すると思ったので(;´Д`)
たかだか2000万レコード程度(データ30GB, インデックス1GB)だったのでそんなの時間はかかりませんでした。

さすがに7時台の山手線は空いてますね。快適です。
ただ会社の清掃時間帯だったので、騒がしくて集中できなかったです。9時頃に出社するのが良いみたい。このくらいの時間だと人も少なくて静かで集中できて良い感じです。朝の2時間は夕方の4時間分に相当するくらいです(`・ω・)

アザゼルさんの最新刊買えました。
アマゾンで買っても良かったのですが、土日じゃないと受け取れないので新宿の地下の本屋で購入(´・ω・)

よんでますよ、アザゼルさん。 3 (3) (イブニングKC)

アマゾンと言えばコンテンツマッチ型のアフィリエイトを左に置いてみたのですが、全然マッチングしていない気がします。英語の本がいっぱい出てくるのはおそらくコードを掲載しているからだと思うのですが、えっちな事書いていないのにアダルト系の本が出てくるのはどうしてでしょうか・・。(´ε`;)

OracleでCLOBに高速アクセスする方法

ちょっと釣りみたいなタイトルです。

LOBに関してはOracleが分厚いPDFファイルを用意しているので、そちらを参照して理解するのが良いと思います。今回はCLOBへアクセスする速度が速くなるわけではなく、いかにCLOBへアクセスさせないようにする方法です。アクセス速度自体はchunkのサイズとか気にすれば良いと思います。

そもそもなぜCLOBを使うのか?

これはVARCHAR2が4000バイトまでのため、AL32UTF8のデータベースの場合、日本語は最大4バイト消費します。(マニュアルによると、4バイトのUTF8は内部で6バイト使うみたいな事が書いてありましたので、ちょっと検証する必要はありそうです。)
ちなみに全角文字が2バイトという神話はSJISやEUC(一部3バイトですが)の時代の話です。UTF8では半角カナや漢字は3バイト、一部の漢字は4バイト使います。

さて、CLOBに入れるデータは常に4000バイト以上とは限りません。4000バイトで収まる場合はVARCHAR2のカラムに入れて収まらない場合はCLOBのデータに入れれば良いと思います。つまり1000バイトの文字列であればCLOBに入れる必要は無いわけです。今回はぱぱっと対応する策(松竹梅で言う梅コース)でどれほど効果があるか確認してみました。

COMMENT_TEXTがCLOBの列です。梅コースのためVARCHAR2の列はありません。LENGTH関数はCLOBに対しては文字数を返します。(LENGTHBはCLOBには適用できません)。CLOB中にはマルチバイト文字が2バイトで入っているという噂なので(UCS2で保存してるの??) 2000で判定しています。(サロゲート文字については未検証)。TO_CHARでVARCHAR2に変換しています。

SELECT
    CASE
        WHEN LENGTH(COMMENT_TEXT) > 2000 THEN NULL
        ELSE TO_CHAR(COMMENT_TEXT)
    END AS COMMENT_TEXT_A,
    CASE
        WHEN LENGTH(COMMENT_TEXT) > 2000 THEN COMMENT_TEXT
        ELSE NULL
    END AS COMMENT_TEXT_B
FROM DIARY_COMMENT

上記のSQLを実行すると1014レコード帰ってきます。
全部CLOBとして取得する場合は3120msかかりますが、上記のSQLでは125ms程度になりました。
※CLOBとしては2レコードしかありませんでした

ものによりますが、4000バイト以上使う割合が少ないものではこういうやり方もありなのではないかと思います。

最速はおそらくVARCHAR2(4000)の列をいくつか作って分割するという方法とかあると思います。設計が美しくないですけど。。。

作業用BGM

作業用BGMとしてニコニコのメドレーを聴くことが多いのですが、最近は同じ系統のものしかなくて若干飽き気味です。というかランキング上位の半分は同じジャンルでなんだかなーと思います。

サントラを買おうと思って未だに買えていないわけですが(そういえばスコアも買ってないな・・)、ニコニコにもYouTubeにも音楽だけのものは無かったので、YouTubeからひっぱってきました。 作曲は谷岡久美さんです。

これはFFCC EOTのボスラッシュというクエストなんですが、ボス用の音楽が2種類入っていたので引っ張ってきました。ゼルダや聖剣伝説が好きだった人は楽しめると思います。

あとはファイナルファンタジーつながりで。
FF13の曲がものすごく(・∀・)イイ!!と好評らしいです。トレイラーにも使われていた曲ですよね。 作曲は浜渦正志さんです。

 

あとは北欧系の曲。

 

お正月番組

HDDレコーダーに取りためた番組を消化してました。
というわけでお正月番組見てました(ノ∀`)

で、当然CMも昔のやつが流れているわけでして、地デジのCMも(ry

そういえば007見ていないなー。

 そのCMをYouTubeで見つけました。

低音で「I said get in」がかっこいい。
ボンド役の人も、人相がやばいでしょ!どう見てもプレイボーイではなく殺し屋の顔です!

さてさて、もうちょっと近所のレンタルが安くなってくれればいいんですが、都内のレンタル料高すぎでしょう。大学生の時は1本200円だったのに(´・ω・`)

 

あと、みなみけを録画していたのでそれを全部見ました。

次はソウルイーターを見ないと・・・。(いつの間にか番組終わってたし)

アザゼルさん買いに行ったら入荷してなかった

アザゼルさん3巻を買いに行ったのですが、入荷していませんでした。 Σ(゚д゚lll)ガーン
個人的にアザゼルさんは最近のヒットです。下品ですが面白いです(・∀・)

よんでますよ、アザゼルさん。 3 (3) (イブニングKC)

というわけでクレイモアの最新刊を購入。
うーん。クレイモアのゲームがDSで出るみたいですが、FFCCのようなアクションRPGかタクティクスオウガのようなシミュレーションRPGで出してくれないかなー・・。

ついでにデッドマンワンダーランドの最新刊も購入。  

 デッドマン・ワンダーランド (5) (角川コミックス・エース 138-12)

謙虚

少し謙虚になるか(´・ω・`)

危機感のなさ

まぁこのエントリは愚痴ですよ(´・ω・)

 

なんだろう、この危機感が感じられない雰囲気は。漠然とした不安ではなくて、項目を列挙できるんです。危険項目として。
それなのに、なんで大丈夫だと思っているのかが分からなくて・・・。

例えば、本当にやりたいなら今のうちに各セクションにネゴっておかないといけないのでは。やりたいやりたいしか情報が無くて企画の大筋すら共有されていなければ、何もできんよ。これ本当に出来るのかな?と思っているなら、最初のテストに入れてテストするもんじゃないの。なんで後回しにして大丈夫と思っているのかがわからない。
私だけで完結できるようなものは、私のレイヤーでどうとでも出来るけどそうでないものにはもっと危機感を持って欲しい。

あと、危ない項目だなと思ってピックアップしてるのに、大項目の中にまた入れちゃうって何。落ち葉の中から危険なものを拾い上げてるのに、それを確認して新しい袋に入れるわけではなくて、また落ち葉の中に戻すっていうのは、どうなのかなー。

 

分からない事だらけ。

となりのブースの列がプランナーの(たぶん)偉い人が集まっているエリアなんですが、みんな自主的に動いて問題解決してるけど、普通はあぁあるべきだと思うんです。(内情知らないので外から見た感じだけですが)。
最近、工程の順番が逆になっていて自主的に動いているのがバカらしくなってくるというか、虚しくなってくる。問題提起してもそれから進展はしないので、もう提起するのも面倒になる。(それが私の明確な責務の1つなら実務時間減らして注力しますけど)

どうやったらもっと危機感もって貰えるのかな。

とか考えると私の心の余裕が無くなってくる。(最近また偏頭痛出てきたし)

 

私の最近の楽しみは、セブンイレブンのほっけを食べることくらいだ。

 

こんな私を救ってくれる奇特な方は、私の愚痴を聞くか、面白いゲーム(360かDS)かDVD(ブルーレイ)を紹介するか、履歴書を会社に送ってください。

Solr

Solrの記事って結構少ないなぁ(´・ω・)

というわけで色々と調べているのですが、一応構成とかスキーマとかはだいたい決まりました。独自フィルタも追加できたので、ちょこちょことメモを残していこうかなと思います。

今苦労しているのは、要件がちょっとめちゃくちゃなのでスキーマの設計が定まらないというか、DynamicFieldをバリバリ使わないと対応ができない事です。DynamicFieldは@Fieldアノテーションを有効活用できないので困ってます。まぁObjectBinderを自分で作れば良いだけなんですが(´・ω・)

MultiCoreの機能を使ってきっちりスキーマ定義を分けようかなー。
ゥ─σ(・´ω・`*)─ン…

Solaris10にlua-5.1.4をインストール

Solaris10にLua-5.1.4をインストールしてみます。

事前にgcc4をインストールしないと警告がいっぱい出ます。gcc4のインストールは別の記事に書いてあるのでそちらを参照。

wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
gtar -zxvf lua-5.1.4.tar.gz
cd lua-5.1.4
gmake solaris
gmake install INSTALL_TOP=/usr/local

無事にビルドできました。

まぁ・・・Luaはパッケージがあるのでpkgaddで入れれば良いと思います。

Solaris10でgcc-4.4.0をインストールする

Lua5.1.4をSolaris10の素のままでビルドしようとすると警告が沢山でるのでgcc-4.4.0をインストールしてみます。ちなみに警告は「visibility attribute not supported in this configuration」といった感じのものでgcc4からサポートされたやつみたいです。staticな関数をライブラリの外に出す/出さないを指定できるgcc拡張のようです。

またgcc4.1以降はlibgmpとlibmpfrが必要との事なので、これらを事前にビルドします。

まずはlibgmpをwgetで取得してgtarで展開します。その後以下の手順でビルド。

cd gmp-4.2.4
./configure --prefix=/usr/local/gcc4 --build=i386-sun-solaris2.10
gmake
gmake check
gmake install

ちなみに、--buildオプションを指定しないとビルドに失敗しました。どうやら「core2-pc-solaris2.10」と認識されていたのが原因のようです。こちらのページを参考にさせていただきました。

次にlibmpfrをビルドします。こちらもwgetで取得してgtarで展開します。その後以下の手順でビルド。

cd mpfr-2.4.0
./configure --prefix=/usr/local/gcc4 --with-gmp=/usr/local/gcc4
gmake
gmake check
gmake install

これでgccをビルドする準備が整いました。
その前に、LD_LIBRARY_PATHに/usr/local/gcc4/libを入れておかないとlibgccのコンパイルでこけます。
gcc-core-4.4.0.tar.gzとgcc-g++-4.4.0.tar.gzを取得してgtarで展開します。

cd gcc-4.4.0

./configure \
--prefix=/usr/local/gcc4 \
--with-ar=/usr/ccs/bin/ar \
--with-as=/usr/ccs/bin/as \
--with-ld=/usr/ccs/bin/ld \
--enable-shared \
--enable-languages=c,c++ \
--with-gmp=/usr/local/gcc4 \
--with-mpfr=/usr/local/gcc4

gmake bootstrap
gmake install

一応確認。

/usr/local/gcc4/bin/gcc -v
Using built-in specs.
Target: i386-pc-solaris2.10
コンフィグオプション: ./configure --prefix=/usr/local/gcc4 --with-ar=/usr/ccs/bin/ar --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --enable-shared --enable-languages=c,c++ --with-gmp=/usr/local/gcc4 --with-mpfr=/usr/local/gcc4
スレッドモデル: posix
gcc version 4.4.0 (GCC)

そして環境変数のPATHにも/usr/local/gcc4/binを入れます。 
Luaのビルドは別の記事で。

MySQLのLIMIT

知り合いよりMySQLの使い方がひどいんですって話があって、その中でLIMITの話があったのでこういう事なのかなーと推測してみた。

その話によるとなんでもかんでもLIMITをつけている。SELECT * FROM XXXX LIMIT 1 みたいな事をいろんなところでやっているという事でした。MySQLはそんなに詳しくないのでDB2のOPTIMIZE FOR 1 ROWみたいなおまじないでつけてるんじゃないのかなーと思いつつ、実際に実行計画をとってみました。

テーブルを専用に用意するのは面倒なので、家のテーブルから適当にデータが入っているやつをチョイス。

mysql> describe BC2_ST_POSTLIST;
+------------+---------------+------+-----+-------------------+-----------------------------+
| Field      | Type          | Null | Key | Default           | Extra                       |
+------------+---------------+------+-----+-------------------+-----------------------------+
| HASH       | bigint(20)    | NO   | MUL | NULL              |                             |
| LINK       | varchar(1024) | NO   |     | NULL              |                             |
| STATUS     | int(11)       | NO   |     | 0                 |                             |
| REGISTDATE | timestamp     | NO   | MUL | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+---------------+------+-----+-------------------+-----------------------------+
4 rows in set (0.00 sec)
mysql> show index from BC2_ST_POSTLIST;
+-----------------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table           | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-----------------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| BC2_ST_POSTLIST |          1 | IDX_BC2_ST_POSTLIST   |            1 | HASH        | A         |     4166553 |     NULL | NULL   |      | BTREE      |         |
| BC2_ST_POSTLIST |          1 | IDX_BC2_ST_POSTLIST_2 |            1 | REGISTDATE  | A         |       41253 |     NULL | NULL   |      | BTREE      |         |
+-----------------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.02 sec)
mysql> select count(*) from BC2_ST_POSTLIST;
+----------+
| count(*) |
+----------+
|  4164791 |
+----------+
1 row in set (0.00 sec)

これにSQLを発行してみる。

mysql> explain select * from BC2_ST_POSTLIST LIMIT 1;
+----+-------------+-----------------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table           | type | possible_keys | key  | key_len | ref  | rows    | Extra |
+----+-------------+-----------------+------+---------------+------+---------+------+---------+-------+
|  1 | SIMPLE      | BC2_ST_POSTLIST | ALL  | NULL          | NULL | NULL    | NULL | 4166649 |       |
+----+-------------+-----------------+------+---------------+------+---------+------+---------+-------+
1 row in set (0.00 sec)

おや?rowsがひどいことになってます。(件数が上のCOUNTと違うのはオンラインで稼働中のテーブルなので・・)。これではLIMITをつけない場合と同じですね。OracleのCount Stopのような実行計画になると思っていました。

MySQLのリファレンスによると、色々かかれていますね。
http://dev.mysql.com/doc/refman/5.1/ja/limit-optimization.html

試しにORDER BYをINDEXをつけている列につけてみました。

mysql> explain select * from BC2_ST_POSTLIST ORDER BY REGISTDATE LIMIT 1;
+----+-------------+-----------------+-------+---------------+-----------------------+---------+------+------+-------+
| id | select_type | table           | type  | possible_keys | key                   | key_len | ref  | rows | Extra |
+----+-------------+-----------------+-------+---------------+-----------------------+---------+------+------+-------+
|  1 | SIMPLE      | BC2_ST_POSTLIST | index | NULL          | IDX_BC2_ST_POSTLIST_2 | 4       | NULL |    1 |       |
+----+-------------+-----------------+-------+---------------+-----------------------+---------+------+------+-------+
1 row in set (0.00 sec)

rowsが1件になりました。
というか、そもそもWHEREやORDER BYを指定しないクエリにLIMITをつける意味が私にはわかりませんでした(´・ω・)
意味というか、それで要件を満たせるユースケースってどんなのがあるかなーと。
ただし、

MySQL が要求された行数をクライアントに送信すると、クエリが中止されます(SQL_CALC_FOUND_ROWSを使用していない場合)。

とあるので、この場合は1件フェッチした場合にクエリが中断されるんだと思います。それを確認してみました。

mysql> explain select SQL_CALC_FOUND_ROWS * from BC2_ST_POSTLIST ORDER BY REGISTDATE LIMIT 1;
+----+-------------+-----------------+------+---------------+------+---------+------+---------+----------------+
| id | select_type | table           | type | possible_keys | key  | key_len | ref  | rows    | Extra          |
+----+-------------+-----------------+------+---------------+------+---------+------+---------+----------------+
|  1 | SIMPLE      | BC2_ST_POSTLIST | ALL  | NULL          | NULL | NULL    | NULL | 4174728 | Using filesort |
+----+-------------+-----------------+------+---------------+------+---------+------+---------+----------------+
1 row in set (0.00 sec)

ふむふむ。実際にEXPLAINじゃなくて実行してみた。
データの中身は見せられないので切り取り。

mysql> select SQL_NO_CACHE SQL_CALC_FOUND_ROWS * from BC2_ST_POSTLIST LIMIT 1;
1 row in set (5.20 sec)
mysql> select SQL_NO_CACHE * from BC2_ST_POSTLIST LIMIT 1;
1 row in set (0.00 sec)

ふむふむ。ということはやっぱり中断はされているっぽいですね。
MySQLってOracleのように何バイト読み込んだかみたいなものって見れないのかな・・。

いいお勉強になりました。
うーん。これくらいじゃ目くじら立てる原因にはならなそうなので他の要因がありそうです(まぁ積もりにつもった様々なイライラだと思いますがw)。テーブルの全データをメモリに載せようとしちゃうのかな。実行計画上は全件スキャンするようになっているし。
これ以上は知識が無いのでソース読んだ方が早そう(´・ω・)

まぁテーブル構造知らないしどんな要件でどんなSQLを発行しているのか分からないので、なんとも言えませんでした(;´Д`)

 

LD_RUN_PATHの確認方法

忘れちゃうのでφ(`д´)メモメモ...

dump -Lv xxxx | grep RPATH

xxxxには実行ファイルを指定。

例えば一個前の記事のttserverに対して実行すると、

[tamtam@tamsvr11 bin]$ dump -Lv ./ttserver  | grep RPATH
[16]    RPATH           /export/home/tamtam/service/tc-1.4.17/lib:/export/home/tamtam/service/tt-1.1.23/lib

 

Solaris10でTCとTTをビルドするメモ

Solaris10でTCとTTが動作するようになってからというもの、最新版をずっと試していなかったので久しぶりにビルドしてみました。ちなみに会社にもインストールしていますが、会社ではLua拡張も組み込んでいます。社内用に構文や機能が追加してあるLua拡張版があるのですが、私は標準のままのLuaを使っています。

今回はLuaをサポートしない場合のビルドメモ。

Luaを組み込んでビルドします。他の記事にLuaにビルドの記事がありますので、Luaについてはそちらを参照。

wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.17.tar.gz
gtar -zxvf tokyocabinet-1.4.17.tar.gz
cd tokyocabinet-1.4.17
./configure \
  CC=gcc \
  --prefix=/export/home/tamtam/service/tc-1.4.17
gmake
gmake install
wget http://tokyocabinet.sourceforge.net/tyrantpkg/tokyotyrant-1.1.23.tar.gz
gtar -zxvf tokyotyrant-1.1.23.tar.gz
cd tokyotyrant-1.1.23
./configure \
  CC=gcc \
  LDFLAGS="-R/export/home/tamtam/service/tc-1.4.17/lib -R/export/home/tamtam/service/tt-1.1.23/lib" \
  --prefix=/export/home/tamtam/service/tt-1.1.23 \
  --with-tc=/export/home/tamtam/service/tc-1.4.17 \
  --enable-lua \
  --with-lua=/usr/local
gmake
gmake install

無事にビルドできました。

これはひどい

http://www.atmarkit.co.jp/fjava/rensai4/webjousiki08/webjousiki08_3.html

@ITってSI業界だと結構参考にされるサイトだと思うのですが(違っていたらごめんなさい)、こういうコードを鵜呑みのにして業務システムが作られていると思うとぞっとします。

つっこみどころとしては、

・PreparedStatement/ResultSetをCloseしていない

ConnectionをCloseした段階でPreparedStatementもCloseされるって?
コネクションプールをしている場合も同じ事が言えますか?
PreparedStatementCacheをしている場合も同じ事が言えますか?
「データベースに接続したら後始末は確実に」という記事を参照しろと書いてあるにもかかわらず、掲載されているコードは参照してかつ参考しているとは思えない。

 

・while(rs.next())の部分

これWhileである必要ないですよね。
Whileで回すって事はIdはユニークではないのでしょうか。
でもCount==1の時のみ処理しています。
何がしたいのか意味がわかりません。こんな冗長で分かりづらいコード書かないでIfで良いのでは。
if(rs.next()){ 一致データを取得 } else{ 認証失敗 }

 

こんなコード出してきたらグーパンチですよヽ(`Д´)ノ

それは最適解か?

満点じゃなくて最高点を。
と、そういう話ではなくて・・・。

昔のゲームはうそんこ物理でそれっぽく見せていました。物理演算を馬鹿正直に計算してもハードウェアの処理性能が追いつかない事と、物理法則を忠実に再現してもそれが単純で面白いとイコールではなかったからです。(と勝手に思ってみる)

話はちょっと変わって、検索エンジンのスコアリングや統計学的に基づいたランキング情報を生成する場合、算数ではなく数学に基づいた計算をします。スコアリングの計算では、TF(出現頻度)やIDF(逆出現頻度)が良い例だと思います。関連を計算する場合も沢山の計算方法があります。
これも目的によると思いますが、算数レベルで考えた数式で良い場合もあれば、それでは最適解を得られない場合もあります。

何が言いたいかと言うと、こういう場合はこうして、こういう場合はこうしてって算数を組み合わせるのも一つのやり方だと思いますが、「どんな状態のもののスコアを高くしたいか」という要件さえあれば、その手法の最適解は餅屋が考えますよっと。
統計学や数値解析、グラフ学とかそういうのがバリバリ分かってるなら数式を提示されても納得がいきますが・・。

餅は餅屋に。

隣の芝生が青く見えない

いろいろな会社の人と話をする機会が以前よりも増えています。
でも、話を聴いても隣の芝生が全然青く見えないのです。

その話や仕事がつまらないというわけではなく、実際面白そうだなぁーと思います。でも羨ましいとまでは思わなくて・・。「面白そうな事やってるなぁ、自分もやろうっと。」って感じで「いいなぁ、自分もその会社行きたいなぁ」とは思わない自分が居ます。

現状に満足しているというわけではなく、やろうと思えば何でも出来る環境が目の前にあるってだけかもしれません。まぁ突拍子な事をやるとさすがに何枚ものフィルタが入って抑止力が働くんですがねw
抑止力が働くまでは好きに出来るとも言う。

 

 

Struts2のJspTemplateEngineで文字化けを起こさないようにする初期設定

前の記事でIncludeクラスのエンコード設定がおかしいため、JspTemplateEngineで文字化けが発生する問題を取り上げましたが、今回はそれを回避するコードを書いてみました。
IncludeクラスのInstanceを1回作成し、setDefaultEncodingを実行すればstaticなフィールドにデフォルトエンコードが設定されるのでContextListenerの中でそういったコードを書きます。

ContextListenerの中でDispatcherへListenerを追加します。そしてそのListenerの中で初期化処理を行います。なぜこういう風にするのかと言うと、Struts2のコントローラーはFilterで実装されています。ContextListenerで直接初期化処理を書いてしまうと、Filterの初期化よりも前に実行されてしまい、Struts2の初期化処理が行われていない状態で実行されてしまう事になるからです。

 

public class DefaultContextListener implements ServletContextListener {
	private DispatcherListener dispatchListener = new DispatcherListener() {

		private ValueStackFactory valueStackFactory;
		private String defaultEncoding;

		@SuppressWarnings("unused")
		@Inject
		public void setValueStackFactory(ValueStackFactory valueStackFactory) {
			this.valueStackFactory = valueStackFactory;
		}

		@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
		@SuppressWarnings("unused")
		public void setDefaultEncoding(String val) {
			defaultEncoding = val;
		}

		@Override
		public void dispatcherInitialized(Dispatcher du) {
			du.getContainer().inject(this);
			ValueStack stack = valueStackFactory.createValueStack();
			new Include(stack, null, null).setDefaultEncoding(defaultEncoding);
		}

		@Override
		public void dispatcherDestroyed(Dispatcher du) {

		}
	};

	@Override
	public void contextInitialized(ServletContextEvent sce) {

		Dispatcher.addDispatcherListener(dispatchListener);

	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {

		Dispatcher.removeDispatcherListener(dispatchListener);

	}

}

 

仕事の仕方

めもめも。

偉い人の影に隠れて好きなことやりまくってるって思われているみたいですけど、面倒くさくて泥臭い事だっていっぱいやっているんですよ(´・ω・) というか、仕事の半分くらいは他の人から見えていないだけでだいたいそういうもんじゃないですか。
やることやった上で好きなことやってるんです。
なので、いっぱいいっぱいの時は面白そうな事には手が回っていないのです。

 

何か決めるとき「じゃぁそれで!」と結構適当な感じに見えているのかもしれませんが、実は結構調べていたりします。ただ、大筋OKならそれで良いじゃないと思うわけです。どんな方法も例外はあるので、そんな所に時間をかけていると何も決まらなくて・・。例外を考えないって訳ではないですよ。当然。

あとは、「とりあえずやってみて、ダメだったら(問題が発生したら)その時考えます」と言うときもあります。方法変えたときや新しい事をやるときには、だいたいメールに書いていたりします。だって発生もしない問題考えてもしょーがないもん!というと適当すぎるので、発生しうる問題は当然予想します。予想出来る事は対応策も考える事が出来るので問題にはあまりなりません。元々Bプラン、Cプランと他の方法も考えてあるのでDEADENDにはならないようにしています。
なので、とりあえずAプランやりましょう。という事になっていく。
ちなみに↑のように一言メールに書いておくと、反対される確率は一気に下がります。

 

あとは、権限的に自分しか出来ない事を誰でもできるようにしてしまう。

サーバの権限ないから自分にはできません・・。
→ 権限あげます。どうぞどうぞ。
→ え、ポリシー的にアカウント作っちゃダメなの? じゃぁ自分のアカウントでプロセス動かしてそれを叩くようにする
「再起動もできます。バンバン使ってOK。それ使って問題が発生した時点でプログラマが対応します」

 

最近困っているのが「XXさんがやった方が早いよね」というパターン。
「たむたむがやれば1日かからないよね」 ←こんな感じ。
締め切りがあと1週間!とかならそれで良いと思うけど、1ヶ月後とかなら別に1週間かけて他の人がゆっくりやっても良いんじゃないかと思います(その人の工数を1週間割いても良い前提で)。仕事の幅ってそうやって増やしていくものなんだと思います。

結構カオスな開発環境で誰かのソースを弄って自分で環境を復旧させてってやっていくと、障害対応スキルのようなものが高くなります。綺麗な環境で一から構築した事しか無い人と、そうでない人とでは対応能力に差が出ると思います。なのでこういう仕事はスキル磨くのにとっても良いのですが、楽しい仕事ではないのでそんな仕事を振るのは忍びないと思うわけで。
難しいね(´・д・)

月末日を求めるプログラム

検索すると結構見つかるものですが、JavaのCalendar#getActualMaximumを使った間違ったサンプルを多く見かけるので正しいやり方をメモメモ。相手の力量をはかるためのクイズとして使っても良いかもしれませんね。

カレンダーがgetInstance()じゃない事は言及しません。
以下は2006年2月の月末日を求めるコードです。

●とりあえず間違ったコード

GregorianCalendar c = new GregorianCalendar(Locale.JAPAN);
c.set(Calendar.YEAR, 2006);
c.set(Calendar.MONTH, 2 - 1);

System.out.println(c.getActualMaximum(Calendar.DAY_OF_MONTH));

何がいけないか。

月末に実行してみるとわかると思います。
仮に今日が2009年3月31日だとします。

まず1行目でCの値は2009年3月31日になります。(時間は省略)
次に2行目でCの値は2006年3月31日になります。
3行目でCの値は2006年2月31日になります。※
→2006年3月3日になります。
2006年3月の月末日は31日のため、31が表示されます。

というわけで、

●正しいコード

GregorianCalendar c = new GregorianCalendar(Locale.JAPAN);
c.set(Calendar.YEAR, 2006);
c.set(Calendar.MONTH, 2 - 1);
c.set(Calendar.DAY_OF_MONTH, 1);

System.out.println(c.getActualMaximum(Calendar.DAY_OF_MONTH));

Subversionのコミットメールをpythonで書いてみた

なぜpythonかと言うと、rubyは入っていないしperlは5.6でNet::SMTP使えないしsendmail行方不明だし・・・。pythonはcvs2svnで必要だったのでインストールしてもらっていたのでした。PERLも5.10をインストールして貰えばいいんですが、依頼をして待つ時間を考えるとpythonで書いた方が早いだろうと判断したからです。

動けばいいやーレベルで書いたので穴があるかもしれないです。ちなみにバージョンは2.6で確認しました。

●post-commit

#!/bin/bash

REPOSITORY="$1"
REVISION="$2"
MAILER="/data/svn/proj1/hooks/mail.py"

export LANG=ja_JP.UTF-8
export LC_ALL=ja_JP.UTF-8

$MAILER $REPOSITORY $REVISION

●mail.py

#!/usr/local/python/bin/python
# -*- coding: utf-8 -*-

import re
import sys
import os
import subprocess
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate

SMTP_HOST = 'xxx.xxx.xxx.xxx'
SMTP_PORT = 25
SVNLOOK = '/usr/local/svn/bin/svnlook'
subject_prefix = "[svn][proj1]"
from_addr = 'no-reply@example.com'
to_addr = ["user1@example.com", "user2@example.com"]


def send(from_addr, to_addr, msg):
    s = smtplib.SMTP(SMTP_HOST, SMTP_PORT)
    s.sendmail(from_addr, to_addr, msg.as_string())
    s.close()

def create_message(from_addr, to_addr, subject, body, encoding):
    msg = MIMEText(body, 'plain', encoding)
    msg['Subject'] = Header(subject, encoding)
    msg['From'] = from_addr
    msg['To'] = ', '.join(to_addr)
    msg['Date'] = formatdate()
    return msg

if __name__ == '__main__':

    REPOS = sys.argv[1]
    REV = sys.argv[2]

    # exec svnlook
    p = subprocess.Popen(["%(SVNLOOK)s info %(REPOS)s -r %(REV)s" % locals()], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    lines1 = p.stdout.read().splitlines()
    AUTHOR = lines1.pop(0)
    DATE = lines1.pop(0)
    lines1.pop(0)
    LOG = "\n".join(lines1)

    # exec dirs-changes
    p = subprocess.Popen(["%(SVNLOOK)s dirs-changed %(REPOS)s -r %(REV)s" % locals()], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    dirchanged = ""
    for dir in sorted(p.stdout.read().splitlines(True)):
        dirchanged += "    " + dir

    # exec changed
    p = subprocess.Popen(["%(SVNLOOK)s changed %(REPOS)s -r %(REV)s" % locals()], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    r = re.compile('^(.).  (.*)$')
    adds = []
    dels = []
    mods = []
    for file in (p.stdout.read().splitlines(True)):
        (tmp, code, path, tmp) = r.split(file)
        if tmp == 'A':
            adds.append("    " + path)
        elif tmp == 'D':
            dels.append("    " + path)
        else:
            mods.append("    " + path)

    body  = "Author: " + AUTHOR + "\n"
    body += "Date: " + DATE + "\n"
    body += "New Revision: " + REV + "\n"
    body += "\n"
    body += "Log:\n"
    body += LOG
    body += "\n\n"
    body += "Direcoties:\n"
    body +=  dirchanged
    body += "\n"
    if len(adds) != 0:
        body += "Added:\n"
        body += "\n".join(sorted(adds))
    if len(dels) != 0:
        body += "Removed:\n"
        body += "\n".join(sorted(dels))
    if len(mods) != 0:
        body += "Modified:\n"
        body += "\n".join(sorted(mods))

    subject = subject_prefix + " r" + REV + " - " + AUTHOR + " -"
    msg = create_message(from_addr, to_addr, subject, body, 'UTF-8')
    send(from_addr, to_addr, msg)

JspTemplateEngineのBug(Struts2.1.6)

結論から言うと、JSPを使ったカスタムタグを作るときにEncodingの指定を読み込めず(システムのデフォルトの文字コード)を使うようになって文字化けが発生するというバグが存在しました。

Struts2.1.6でカスタムタグを作るときにデフォルトではFreeMarkerを使うようになっていますが、VelocityやJSPもテンプレートとして使うことができます。struts.xmlでデフォルトのエンジンを変更するか、自作のカスタムタグでテンプレート名を明示的に拡張子付きで指定してあげます。(例:hogehoge.jsp)

さて、JspTemplateEngineはRequestDispatcherのIncludeを使って実装されています。
このIncludeをする部分はカスタムタグのInclude(<s:include>)のstaticなメソッドであるincludeを呼んでいます。

ソースをひもといていくと、、、

private static String defaultEncoding;
@Inject(StrutsConstants.STRUTS_I18N_ENCODING)
public void setDefaultEncoding(String encoding) {
    defaultEncoding = encoding;
}

Σ(´Д`lll)エエ!!

しかも、defaultEncodingとは別にencodingってフィールドも持っているのですがこれもstaticなわけです。
これはひどい実装だ・・。 分けている意味が分からない。。。defaultがstaticでencodingがnon-staticだったら意味はわかるのですが・・・。

しかも、struts-default.xmlのBeanの定義には↓こう書いてある。

<bean class="org.apache.struts2.components.Include" static="true" />

staticなinjectionをしますよと書いてあるのにかかわらず、Setterはstaticではないのです!!
誰がこんなコードを何のために修正したんだろうと思ってレポジトリ追ってみた。

http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/components/Include.java?r1=554226&r2=577728

ここで変更されたみたい。

 

ちなみに、先に<s:include>タグを実行すると、インスタンス化されて普通に@Injectが走ってstaticフィールドに設定されて以降はその文字コードを使うようにします。WTPだとデフォルトでfile.encoding=UTF-8をつけるみたいで気がつきませんでした。

(○口○*) はぁ・・

ミート矢澤に行ってきました

今月頭の話になりますが、ミート矢澤に行ってきたのです。
このお店は五反田の目黒川沿いにあるお店でして、A5ランクの牛肉しか扱っていないのです。

金曜日の20時頃に行ったのですが、まぁそんな時間でも並んで居るわけでして・・・40分ほど並びました。

頼んだサイドディッシュはこちら↓

 

そして頼んだメインディッシュはハンバーグ+目玉焼きのトッピング。
見た目ペッパーランチと変わらないとか言わないように!!(ペッパーランチ行ったことないですけど)

φ(c・ω・ )ψ モグモグ

ちょー美味しい(ノ´∀`*)

タレつけなくても美味しい味がします。
そして今まで食べたハンバーグの中で1,2位を争います!!
(個人的にはとろけるチーズのトッピングをやってくれたら最高です)

 

締めはアイス。
本日のデザートを聞いておきながらアイスを頼む。

ごちそうさまでした(*´∀`)

技術を継承する話

連続で投稿です。さっきの話は業務の話でした。今回は、技術を啓蒙、伝承していく話です。
特許やビジネスサービス化の話ではありません。

ある技術Xがあるとして、この技術Xを覚えたAさんが居るとします。
Aさん以外は技術Xに明るくないと仮定します。

さて、この技術Xはとても素晴らしいものですが、周りはXについて明るくないのでその必要性や優位性の理解に苦しみます。

ここで選択肢が登場。
1. この素晴らしい技術Xを使えば自分が優位に立てる!! 独り占めだ!
2. この素晴らしい技術Xを周りに啓蒙して一般化しよう。(バズワードになりつつあるコモディディ化とでも言えばいいのかな)

 

仮に1を選んでしまったとします。(選んでしまったと書くとこっちはBAD ENDな匂いがプンプンします)
※ここで言う理解とはアルゴリズムやアーキテクチャと言った局所技術的なものではなく必要性や優位性に対しての理解です。

AさんはXを使って素晴らしいものXXを構築する事ができました。
Aさんスゲー!! となるわけですが独り占めしているため、ある意味ブラックボックス化されているのでXXの仕組みはなかなか理解できません。あまり知られていないものは、それ本当に大丈夫なの?と思うのが世の常です。

技術革新は速いもので半年もたてば次の新しい技術が提案されています。この新しい技術をYとします。
YはXの良くない部分を改善した、さらに新しい技術です。ただしYの良さを理解するにはXの理解が前提となります。

Aさん以外はまだXの理解にすら苦しんでいる状況でYの理解なんて到底できるわけがありません。
ましてや、本当に大丈夫なの?と思っているものを前提としているので、Yの理解への道はさらに険しくなってしまいます。
技術YはAさん以外から見れば1歩ならず2歩も3歩も進んだものなので、突拍子なものに見られてしまいます。

ここで慌ててAさんはXから啓蒙したとしましょう。Xに理解を示して貰える頃には次の技術Zが出ています。ここで小さな情報格差?が生じます。AさんはZを使いたくてもYの時と同様な事が起こりすんなりと使うことができません。

(少々強引なストーリーですが)BAD END!!

 

2を選ぶと、技術Zが出てきてもAさん以外もXやYの優位性に理解を示しているのでZをすんなり使いやすくなります。
またはその採用の是非に関して対等な議論の展開ができます。
(こっちのストーリーを書くの面倒になったので省略)

(こっちも強引ですが) HAPPY END!!

HAPPY ENDってそこで終わってしまうから嫌ですね。
YOU GOT A HAPPY LIFE!!

 

と言うわけでかなり強引ですが、自分が持っている技術は出し惜しみしないで周りに教えていこう。そして自分はさらなる高みを目指そうというお話でした(自戒の意味も込めて)。新しいもの好きなら特に。
「プログラマブルシェーダ4.0でジオメトリシェーダもバリバリ試そうぜ!」「えー2DでDirectDraw(dx7)で十分だよ」なんて状況は悲しいです。

※新しいものが常に良いとは限りませんし、それがオーバースペックになる場合も多々あるので、これが正しいというわけではありません!!

 

仕事を握って保身する悪循環について

よく、仕事を握って他の人が出来ないようにして保身を謀る人が居るかと思います。
個人的にこの仕事の仕方は好きじゃないのですが、今回はこれがどういう悪循環をもたらすか書いてみようと思います。

1. 仕事を握ってその人(以下Aさん)しか出来ないようにする。
 → 例えば、ドキュメントを書かない
 → 例えば、ソースをアップしない
 → 例えば、仕事を振らない

2. Aさんしかその仕事が出来なくなる。
 → Aさんの機嫌を損ねたり、解雇したりすると業務が止まる。
 → Aさんは事実上保身に成功したことになる。

さて、ここからが悪循環の始まりです。

3. Aさんが休むと業務が止まるので、Aさんは休暇が取れなくなる。RVで連休を取るなんて以ての外。

4. Aさんはその仕事から離れることはできないので、新しい案件が来てもアサインされる事が無くなる。
 → 新しい案件はベテランの仕事を握っていない人や若い人、手が空いている人がアサインされる。
 → 新しい案件にAさんをアサインすると、また仕事を握られてしまい業務に支障が出るのでアサインしなくなる。

5. 他の人はどんどん新しい案件や楽しいことをやっている傍ら、Aさんは今まで自分が守っていた保守案件が多くなり開発案件はほとんど無くなる。

という事になるのです。会社や部署によって仕事の性質が違うので「必ずこうなる!」という訳ではありませんが、常に新しいものを作っていく職場では少なくともこういう循環は起こりえるのではないかと思います。

なので、仕事をする時は自分以外の特殊な技能を持っていない人でもその作業が出来る仕組みを用意する。またはそういう作りにする。技術者集団が集まっているような職場の場合、自分の専門分野なんて他の人は普段やっていないだけで大抵できるものです。なので個々の技術なんかよりも、万人が幸せになれる開発環境やワークフロー、コンテンツパイプラインなんかを構築できる人が重要になってくるのではないかと思うのです。

ちなみに、こういう考え方を啓蒙すると休みたいときに休めるようになるはずですヽ( `・ω・)ノ(うん、きっとなるよ!! ・・・なるといいなぁ・・)
「おまえの代わりはいくらでも居るんだ。クビ!」なんて言われたら、そのフローや仕組みを構築できる人が他に居るか周りを見渡してみましょう。居るならそれでよし、居ないならプギャーと思って楽しい職場に移りましょう。

 

なんで延びているのか分かっているのかな・・・

何かしらの理由で公開日が延びたとします。

何かしらって、ほとんどの場合は決まっていない事があるからなんですけどね。基本は仕様を決めてから実装に落とし込んでいくわけですが、実装に遅れが出ている場合やバグが出ている場合は今回の話に当てはまらないので言及しません。

今回は決めなければいけないことが決まっていない場合の話です。

  1. 決定すべき事項が決まっていない
  2. 決まっていないので開発の締めが延期になる
    (延期の理由は決まっていないから。つまり延期した分で決まっていない事を決めないといけないわけです。当然ですね。)
  3. それなのに、延期になったから時間に余裕が出来たと勘違いする
  4. アレもコレもやりたいと仕様を追加したり変更したりする。挙げ句の果てに今まで決定してあるものを覆す
  5. そういう部分は比較的さっさと決めるのに、1の部分は決まっていない。または4で未決事項が増えている
    無限ループの完成

バージョンアップ計画(←オンラインの場合)もマイルストーンも切っているのに、意味なくない?

出来ないものは次回に回す(オフラインパッケージは別として)。何のために締めを切ってるの。何のための計画なの。。

動画配信システムをまじめに考えよう メモ書き

Windowsをエンコードサーバとして使う場合、ffmpegで無くても良いのでコマンドラインでも制御できるソフトを使ってみました。

まずFrapsでラスレムのベンチマークの動画を撮ってみました。
解像度は1280*720、ビットレートが1411kbps、約3分で546MBです。
確かに生データで運用するとすぐに破綻してしまいそうです。

次にMP4に変換してみました。正確にはコンテナをMP4にして動画はH264、音声はAACです。aviutlのpluginでx264出力をしてみました。画質はほぼ劣化無しで13MBになりました。
ス..ス...(゚Д゚(゚Д゚ノ(゚Д゚ノ)ノスゲー!!!

ただ高画質設定で出力したのでエンコードの時間が半端なかったです。3分の動画なのに(´・ω・`) 用途的に長い動画をアップする事もないだろうし、アップロードされる頻度も少ないと見込んで高画質で出力するようにします。
社内の開発機と同程度のスペックをVMWare上に作成してもう1回試してみます(さすがにノートでエンコードは時間かかる)

参考にしたサイトはこちらになります。
エンコード設定(中画質~高画質) - ニコニコ動画まとめwiki
aviutl_h264 - ニコニコ動画まとめwiki
拡張 x264 出力(GUI)の設定項目とその機能について - ニコニコ動画まとめwiki

次はフロントエンドの口を考えてみます。最初はSambaでコピーすれば良いと思っていましたが、フォルダを監視中にコピー中のものを拾ってしまうようなのでやっぱりWEBを口にした方が良さそうです(アップロード完了のタイミングが拾えるので)。不便になりますが触る場所が1カ所になるというメリットもあるという事で勘弁していただきます(´・ω・)

当初はJavaで作っていましたが(Windowsをフロントにする予定だったので、環境構築が楽)、フロントはUNIX系のOSになりそうなのでPHPを使ってみます。Flashプレイヤーから叩く口が3,4個とWEBページも3,4ページくらいなのでJavaは不要だと判断しました。(Javaは比較的開発初動速度が遅いので・・・)(←※VMの起動速度ではなく、開発の初動速度です)
プログラムも特に設計することなく、思いついたままに書いていきます。(設計はDBも含めて10分くらい)

PHPはJavaと違ってアップロードしているデータのStream処理が標準では行えないみたいです。これではアップロードの進捗バーが出せません。。。
・・・まぁいいか(´・ω・)r LAN内ならそこそこ速いしちょっと待てば良いだけだし。

うーん・・モヤモヤがとれないので、たぶん何か抜けがあると思います(´・ω・)

とりあえずエンコードサーバをFedora10 Quad9650 Mem8G上のVMwareにCoreを2個 Mem2GBを割り当てて構築中。
追記:VGAとかチップセットを認識してくれません・・・。ゥ─σ(・´ω・`*)─ン…

さらに追記:
仕方ないので別のPCでエンコードしてみました。やっぱりH264のエンコードは結構時間かかるかも。ちなみにMP4/H264は問題なくFlashプレイヤーで再生できました。また画質を落としてエンコードすればかなり高速にエンコードできます。

というわけで、速度を要求する人の欲求も満たすには、2種類出力すれば良いのでは。で低画質出力タスクをキューに入れるときは優先度を上げればシステムとしては良い感じ。ただ、ここまで作り込む段階ではないでしょうと思うのでこんな事は後回し。

あとは動画の情報とサムネイルを簡単に取得する方法を考えないと・・・。動画の情報はffmpeg使わないと無理かなぁ。。エクスプローラーに情報が表示されるから、そこから取得する方法もありそう。
もしくは、真空波動研のDLLを使えば確実かも。

サムネイル生成はこれかな
http://hp.vector.co.jp/authors/VA033749/soft/azmthumc.html

全部ffmpegで出来るみたい。
http://mitc.xrea.jp/diary/088

そうなるとffmpegでh264出力もやってしまったほうが良いことになるけど、ffmpegの方は調整が大変そうだしとりあえずここは後で考える!!

動画配信システムを作ってみた

とある理由で自分の近くにニーズがある事を金曜日に知ったのでちゃちゃっと作ってみました。
細かい所は何も考えていません。最低限の一次要求を満たしているだけです。

以下使ったものリスト

  • FFMPEG(動画エンコード)
    • たぶん動画はFrapsで取るんだろうなぁと勝手に思ったのでサーバで適切な形にエンコード
    • Sambaフォルダ監視してタスクキューに突っ込んでFFMPEGを実行していく
    • アップロードはWebからではなくエクスプローラーでポイ!
  • RED5(FLV配信) 別になくても大丈夫
  • FLEX BUILDER3(クライアント作成)
  • WEBシステム(コメントを投稿/取得したりする口) ここは慣れている構成で。

モチベーションは、会社では当然ですがソフトウェアをライセンス管理しているので個人で購入したものをインストールしてはいけない。 → FLEX BUILDER3の個人ライセンスのやつインストールできない → FLEX BUILDER3使えばこんな簡単に作れます(実質1日) → これ個人ライセンスのやつで作ったので継続的にメンテは厳しいですねー → 予算で購入にこじつける

というとても歪曲した欲望から来ています。(まぁBUILDERなくてもSDKはフリーなので普通に作れちゃうんですが・・)

 

以下作ってみた画面です。

動画のソースはニコニコから拝借してきました。

ニーズの背景上、コメントが流れても意味がないのでFlickerのような感じで文字等をタイムラインに置けるようにもしてみました。そして○と□の矩形描画もサポート。動画を一時停止させてそのコマ(から数秒間にわたって)に落書きができるというイメージです。

 

ちなみに表示タイミングはCuePointで制御しています。

 

Tokyo Tyrantが公式にSolaris対応しました

Tokyo CabinetもTokyo TyrantもSolaris10で動作するようになりました。ビルドもすんなり通ります。

\(^o^)/ヤター!

Luaをくっつけて色々試してます。

 

Solaris8やSolaris9では動かないですって・・・?
知らない!!!

Solarisは8から/dev/poll使えるのでそれで・・・(´∩ω∩`)

tokyo tyrantのSolaris対応(event ports)を書き直してみた

前回の記事の対応ではスピードが出ません。
というわけでちょっとport_getnの所を修正しました。
make checkでそこそこの速度で全部通るようになりました。

あ、補足ですがtokyo cabinetの最新版はSolaris10で何も指定しなくても普通に通るようになりました。

今回対象にしたのは、tokyotyrant-1.1.14です。(今日付で最新版が出ていますが対応は基本的に同じでいけると思います)

開発環境はSolaris10 x86_64です。uname -aの結果は、
「SunOS tamsvr11 5.10 Generic_137138-09 i86pc i386 i86pc Solaris」です。

というわけで以下修正ソースです。

 

Continue reading

桁の読み間違い

0の数が多いと読み間違える事ってありますよね(´・ω・)
そんな恥ずかしいお話。

ソースを斜め読みしたら、たまたま↓のコードが目に入りました。

  if(gettimeofday(&tv, NULL) == 0){
    ts.tv_sec = tv.tv_sec;
    ts.tv_nsec = tv.tv_usec * 1000 + TTWAITREQUEST * 1000000;
    if(ts.tv_nsec >= 1000000000){
      ts.tv_nsec -= 1000000000;
      ts.tv_sec++;
    }
  } else {
    ts.tv_sec = (1ULL << (sizeof(time_t) * 8 - 1)) - 1;
    ts.tv_nsec = 0;
  }

あれ。
→ 桁上げ処理の所がwhileじゃなくてifだ。
→ 1回しか処理していないね。
→ TTWAITREQUESTの値ってなんだっけ
→ defineで200固定なのね
→ あれ。じゃぁwhileで何回か繰り上げないとダメじゃん!
→ ・・・。
→ ( ゚ ρ ゚ )ボー
→ ・・・
→ 0の数1個間違いました。ごめんなさい。

 

寝ぼけ眼でソース見るのは危険です。もう1回二度寝します( ・д⊂

Tokyo TyrantをSolaris10で使えるようにする eventports編

※書き直しました。(2009/2/16)
http://mt.orz.at/archives/2009/02/tokyo-tyrantsol-2.html こちらの記事をどうぞ。

 

最初に断っておきますが、このやり方はとてもよろしくない方法で対応しています。
本来ならばEPOLL関連の関数に準ずる形でエミュレートするのが正しい方法だと思うのですが、力不足によりttutl.cの中に#ifdefでSolaris用に処理を追加するという暴挙に出てしまいました(´・д・`)
ですので、参考にしてはいけません。

言い訳をするとですね、waitの中でONESHOTフラグでない場合は再登録(port_assosiate)するという方法が動かなかったのです。後できちんと調べてmyconfの修正だけでいけるようにガンバリマス(*・ε・*)

・・・今書いてて思うのですが、ttの場合エミュレートしないといけないepoll系の関数は3つしかなくて、epoll_create()は最初の初期化、epoll_ctlは監視の登録/解除、epoll_waitは読み込み可能なFDを返す、という至極単純な動作なので自前である程度のフラグを管理すればどんな方法でも動くはずだと思いました。

というわけでソースを載せていきます。

Continue reading

ヘッドスパ

    〃〃∩  _, ,_
     ⊂⌒( `Д´) < ヘッドスパ行きたいよぅ
       `ヽ_つ ⊂ノ
              ジタバタ

 

    〃〃∩  _, ,_
     ⊂⌒( `Д´) < リフレッシュしたーい!
       `ヽ_つ__つ
              ジタバタ

 

      _, ,_
     (`Д´ ∩ < 頭コネコネしてもらいたいよー!
     ⊂   (
       ヽ∩ つ  ジタバタ
         〃〃

 


    〃〃∩  _, ,_
     ⊂⌒( つД´) < でもいつもの人がやめちゃったよ・・
       `ヽ_ ノ ⊂ノ
              ジタバタ

 

       ∩
     ⊂⌒(  _, ,_) < あの人の力加減じゃないと・・
       `ヽ_つ ⊂ノ
              ヒック...ヒック...

 

       ∩
     ⊂⌒(  _, ,_)
       `ヽ_つ ⊂ノ  zzz…

 

tokyo cabinet1.4.4をsolaris10でコンパイルする

最新版をSolaris10でコンパイルしてみたです。
いつの間にかSunOSの定義でごにょごにょと分岐が追加されていました!ありがとうmikioさん!(もしかしてこのBLOG見てるのかな(*´∀`))

これでSolaris10でもすんなりビルドが・・・通りませんでした(´・ω・)
堅牢なOSなのは認めるし最近のコンテナやZFS、Dtraceはすばらしい技術だと思うけど、正直なところビルドくらい何も考えないで通って欲しい。。゚(゚´Д`゚)゚。  お願いしますよ。Sunマイクロシステムズ様。

で、やっぱりrealpath関係で怒られました。

Solaris10でrealpathが使えるようになるには、以下の条件を満たす必要があるみたいです。

#if defined(__EXTENSIONS__) || \
        (!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \
        defined(_XPG4_2)

std=c99をつけると、 __STRICT_ANSI__マクロが定義されます。
/usr/include/sys/feature_tests.hをひもといていくと、_STRICT_STDCが定義されるので上記の条件(の2行目のやつ)を満たさなくなってしまうようです。ここ(manpage)を見ると色々書いてあります。

これを回避するには以下の2つの方法があります。(正確にはもっとありますけど、試していません)

  • __EXTENSIONS__を定義する
  • std=c99をstd=gnu99にする

どれくらい影響範囲があるのか分からないので自己責任で好きな方を選択すれば良いと思います。

前者の場合は、以下のようにconfigureで指定します。

./configure CFLAGS="-D__EXTENSIONS__"

後者の場合は、configureとconfigure.inに記述されている内容を置換します。(configureした後にMakefileを修正しても良いですけど)

perl -i -p -e "s/c99/gnu99/g;" configure*

あとは普通にビルドをします。

gmake >/dev/null
tcutil.c:7401: 警告: 初期化子のまわりのブレースを欠いています
tcutil.c:7401: 警告: (`tcglobalmutexonce.__pthread_once_pad' の初期化は不完全です)
tcutil.c: In function `tcbwtdecode':
tcutil.c:7717: 警告: statement with no effect
tcutil.c: In function `tcbwtencode':
tcutil.c:7677: 警告: statement with no effect
ar: creating libtokyocabinet.a

出ている警告は全て動作に対して影響はありません。最初の2個の警告は前回の記事に回避策が書いてあります。(でも、わざわざ修正するような内容ではありません(*・ε・*))

ちなみにマクロの定義一覧を出したい場合は以下のような感じで出せます(2個ほどマクロ定義してあるのはTokyo Cabinetビルドしたらこのマクロももれなくついてきたので。)。実際のソースに対してやると何千行も出てきてしまいますが、以下の場合はその環境で最低限定義されるものが出てきます。(100行もないはずです)

 gcc -std=gnu99 -D_GNU_SOURCE=1 -D_REENTRANT -E -dM -x c /dev/null

 

ちょっと気になった点について。
1.4.2,3,4辺りでpthread_yield()の実装が毎回変更されていて今はusleepになっていますが、これってやっぱりusleepのが速いからなんでしょうか。Solaris10の場合、sched_yieldの実装はシステムコールのthr_yieldになっていて、これがスレッドセーフだから遅いのと、yieldすると必要ない場合でもコンテキストスイッチが走るから・・・なのかにゃ(´・ω・)

 

ガチな試遊会

とあるゲームの試遊会に参加した時の話です。

てきとーな感じで試遊するものだと思っていたのですが、

炎はオブジェクトつかっているみたいだね。炎を上から写してみて。背景焼き込みかな。あれハイポリのままだよね。火花飛ぶ方向が逆。スペキュラが云々・・。

Σ【*゚д゚*】

 

FFCC EOT

買いましたよー。
DSとWii両方とも。

土日おもいっきりプレイしました。

面白いです。
(ノ。・ω・)八(。・ω・。)八(・ω・。)ノ

1週目のボスは倒しまして、2週目に行きたいのですが図書館で前作のボスと戦えるのですが、そのラスボスが倒せないのと、FFCCではおなじみのカニさんが倒せないので、それを倒したら2週目行きたいと思いますヽ( `・ω・´)ノ

Wifiでマルチプレイもできるみたいなので、一緒にやりましょう。
(マルチの画面まだ見ていないので、特定の誰かと出来るのかは知りませんけど。。)

全社的共通のミドルウェア

今日は飲み会でした。写真撮るの忘れた ガ━━ΣΣ(゚Д゚;)━━ン
上司の意外な一面が見れてびっくりw

 

さて、タイトル的に・・
Mayaのプラグインとかシェーダとかそういう話ではありません。

ネットワーク系のプログラムを作るとき、そのプログラムを単なるプロセスとして扱いプログラマが運用を担保するか、もしくはミドルウェアとして開発が担保しないかの2つに分けることができます。

例えば弊社の場合、Oracleはミドルウェア扱いみたいです。ミドルウェアなのでプログラマ(開発側)がインストールをする事はありませんし、日々の運用は別の部署が主導的に行ってくれます。

ではMemcachedについては?
今までのCacheシステムは独自に作っていました。(とあるカンファレンスでも言いましたけど、相当カオスです)
独自のシステムはミドルウェアではなく、ただのユーザープロセスに過ぎません。

Memcachedはいろいろな部署で使われるようになったのでミドルウェア扱いになりつつあります。ただしMemcachedは揮発性のストレージです。・・・ストレージというとかなり語弊がありますね、キャッシュです。キャッシュなので落ちても止まらないシステムにするのが普通だと思います。はい。
しかし、DBでは性能的に扱えないけれど永続的にデータを保存しておきたい場合があります。そんな場合に採用したいのがTokyo Tyrantです。

なぜTokyo Tyrant?
Memcachedプロトコルを解釈できるからです。(個人的な理由として)

ではうちの会社でTTをミドルウェアとして認めて貰うにはどうすればいいのでしょうか。
という事を話してみました。

 

結論から先に言うと、永続性を確保するためのプログラムをミドルウェア扱いするための優先順位としては、

  1. 素のMemcached(これは現状)
  2. Memcached + PluginModule + DBM
  3. (越えられない壁)
  4. Memcached + DBM (ソースを改変してパッチとして)
  5. Tokyo Tyrant (in Solaris)

結局のところ、オープンといえどほぼ1人で作っているものについては採用しがたいとの事でした。(これについてはTTだけでなくSENNAにも言えるみたいです・・) また既存のものにパッチを当てる方法も、その人が結局会社から居なくなった時に誰も手をつけられない状況になるという過去の遺産のせいで受け入れがたくなっています。
このことから、TTをSolaris対応しても私が個人的にパッチを書いている限り、それをSolarisで動作させるよりもLinuxのOSを入れてしまった方が長期的な運用を考えると良いという事でした。

別にSFあたりでDBMを書いても良いわけですが、それだと結局のところ個に縛られることには変わりないわけでして現実的な所を考えると、

  • MemcachedのPluginModule機構の中で永続化する
  • TTのSolaris対応のパッチを公式に取り込んで貰う

という事になりました。

道のりは険しそうです。
ちなみに、普通のプロセスとして動作させる分には何も問題はないです。

 

酔っているので何書いているのか分からなくなってきました。
お風呂入って寝ます。

svnkitを使ってSubversionを操作する

例えばツールで、何かをバージョン管理したい時はバックエンドにSVNを使っていたりするのですが、Davによる自動コミットがONになっていない環境だとファイルの更新が面倒です。ファイルの追加はSVNコマンドでIMPORTを使えば可能ですが、既に存在するファイルの更新は事前にローカルのワークファイルが存在しないとコミットできません。

というわけで、既に登録されているファイルを、チェックアウトしなくても更新できるプログラムを書いてみました。
本当はきちんとクラス化してあるのですが、長くなるので主要な部分を切り貼りしていきます。

// SVNKitの初期化
DAVRepositoryFactory.setup();
// 引数にはSVNのURLを指定します
// 認証とかの設定をしてレポジトリオブジェクトを返します
private SVNRepository getRepos(String baseUrl) throws SVNException {
	SVNURL url = SVNURL.parseURIDecoded(baseUrl);
	SVNRepository repos = SVNRepositoryFactory.create(url);
	if (userName != null) {
		ISVNAuthenticationManager authManager =
			SVNWCUtil.createDefaultAuthenticationManager(
					this.userName,
					this.password == null ? "" : this.password
			);
		repos.setAuthenticationManager(authManager);
	}
	return repos;
}

まずはファイルをSVNに追加するコードです。

/**
 * ファイルをSVNに追加する。
 * 実際に追加される場所は、baseUrl + filePath
 * @param baseUrl SVNのベースのURL
 * @param filePath ファイル名
 * @param fileData 追加するデータの内容
 * @param logMessage コミットログの内容
 * @return コミットログ
 */
public SVNCommitInfo addFile(String baseUrl, String filePath, byte[] fileData, String logMessage) {

	SVNCommitInfo cInfo = null;
	try {

		SVNRepository repos = getRepos(baseUrl);

		ISVNEditor editor = repos.getCommitEditor(logMessage, null, true, null);
		try {
			editor.openRoot(-1);
			editor.addFile(filePath, null, -1);
			editor.applyTextDelta(filePath, null);
			SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
			String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(fileData), editor, true);
			editor.closeFile(filePath, checksum);
			editor.closeDir();

			cInfo = editor.closeEdit();
		} catch (SVNException e) {
			editor.abortEdit();
			throw e;
		}

	} catch (SVNException e) {
		throw new RuntimeException(e);
	}

	return cInfo;
}

次はSVNのファイルの内容を更新するコードです。 上の登録のコードもそうですが、基本は差分(DELTA)を計算してそれを送信しているだけです。

/**
 * SVNのファイルの内容を更新する(変更する)。
 * 実際に変更されるファイルの場所は、baseUrl + filePath
 * @param baseUrl SVNのベースのURL
 * @param filePath ファイル名
 * @param fileData 変更後のデータの内容
 * @param logMessage コミットログの内容
 * @return コミットログ
 */
public SVNCommitInfo updateFile(String baseUrl, String filePath, byte[] fileData, String logMessage) {

	SVNCommitInfo cInfo = null;
	try {

		SVNRepository repos = getRepos(baseUrl);

		// 古いコンテンツを取得する
		ByteArrayOutputStream oldOut = new ByteArrayOutputStream();
		repos.getFile(filePath, -1, SVNProperties.wrap(Collections.EMPTY_MAP), oldOut);
		byte[] oldData = oldOut.toByteArray();

		ISVNEditor editor = repos.getCommitEditor(logMessage, null, true, null);
		try {
			editor.openRoot(-1);
			editor.openFile(filePath, -1);
			editor.applyTextDelta(filePath, null);

			SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
			String checksum = deltaGenerator.sendDelta(
					filePath,
					new ByteArrayInputStream(oldData),
					0,
					new ByteArrayInputStream(fileData),
					editor,
					true
					);
			editor.closeFile(filePath, checksum);
			editor.closeDir();
			cInfo = editor.closeEdit();
		} catch (SVNException e) {
			editor.abortEdit();
			throw e;
		}

	} catch (SVNException e) {
		throw new RuntimeException(e);
	}

	return cInfo;

}

複数ファイルを変更する場合、上記のメソッドを複数回呼び出すと別々のコミットになってしまいます。 まぁeditor.closeEdit()を複数回呼んでいるので、当然と言えば当然ですが。

というわけで、複数のファイルを1回で追加・更新するコードを書いてみます。
その前にファイルが存在するかを確認するメソッドを追加します。

public boolean exists(String baseUrl, String filePath) {
	try {

		SVNRepository repos = getRepos(baseUrl);
		SVNNodeKind nodeKind = repos.checkPath(filePath, -1);
		if (nodeKind == SVNNodeKind.NONE || nodeKind == SVNNodeKind.DIR) {
			return false;
		}

		return true;

	} catch (SVNException e) {
		throw new RuntimeException(e);
	}
}

複数のファイルを良い感じに更新するメソッドです。
BLOGにコードを書き写した時に手動で一部のメソッドを書き換えているのでコピペしても動かないかもしれません。

事前にexists等を求めているのはlockメソッドでエラーが出るからです。SVNKitのバグかどうかは知りません。

public static class SvnRequestEntry {
	public String filePath;
	public byte[] fileData;
	boolean exists;
	byte[] oldData;
}

/**
 * 複数のファイルをSVNに追加する。既にファイルが存在する場合は更新される。
 * また、コミットは1回で行われる。
 * @param baseUrl SVNのベースURL
 * @param entries 追加するデータのリスト
 * @param logMessage コミットログの内容
 * @return コミットログ
 */
public SVNCommitInfo putFiles(String baseUrl, List<SvnRequestEntry> entries, String logMessage) {

	SVNCommitInfo cInfo = null;
	try {

		SVNRepository repos = getRepos(baseUrl);

		// 事前にチェックを行う
		for (SvnRequestEntry entry: entries) {

			SVNNodeKind nodeKind = repos.checkPath(entry.filePath, -1);
			entry.exists = !(nodeKind == SVNNodeKind.NONE || nodeKind == SVNNodeKind.DIR);

			// 古いコンテンツを取得する
			if (entry.exists) {
				ByteArrayOutputStream oldOut = new ByteArrayOutputStream();
				repos.getFile(entry.filePath, -1, null, oldOut);
				entry.oldData = oldOut.toByteArray();
			}

		}

		ISVNEditor editor = repos.getCommitEditor(logMessage, null, true, null);
		try {

			editor.openRoot(-1);
			for (SvnRequestEntry entry: entries) {

				String filePath = entry.filePath;
				byte[] fileData = entry.fileData;
				boolean exists = entry.exists;
				byte[] oldData = entry.oldData;

				// 存在チェック
				if (!exists) {

					// 追加する
					editor.addFile(filePath, null, -1);
					editor.applyTextDelta(filePath, null);
					SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
					String checksum = deltaGenerator.sendDelta(filePath, new ByteArrayInputStream(fileData), editor, true);
					editor.textDeltaEnd(filePath);
					editor.closeFile(filePath, checksum);

				} else {

					// 更新する
					editor.openFile(filePath, -1);
					editor.applyTextDelta(filePath, null);

					SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
					String checksum = deltaGenerator.sendDelta(
							filePath,
							new ByteArrayInputStream(oldData),
							0,
							new ByteArrayInputStream(fileData),
							editor,
							true
							);
					editor.textDeltaEnd(filePath);
					editor.closeFile(filePath, checksum);

				}
			}
			editor.closeDir();
			cInfo = editor.closeEdit();

		} catch (SVNException e) {
			editor.abortEdit();
			throw e;
		}

	} catch (SVNException e) {
		throw new RuntimeException(e);
	}

	return cInfo;

}

使い方はこんな感じです。他のメソッドも同じ感じです。

SVNCommitInfo info1 = addFile(
	baseUrl,
	"test01.txt",
	"test01_init_data".getBytes(),
	"プログラムからからファイルを追加してみる\nテスト。"
	);

putFilesを呼び出すときは、SvnRequestEntryのfilePathとfileDataに値を入れて、 更新したいファイルの数だけList化すれば良いです。

ゥ─σ(・´ω・`*)─ン…

ONESHOTをきちんとエミュレート出来ていないのが原因でした。

CLOSEに何回か失敗するのが気にならないのであれば、op=MODの時何もしなければ良いだけなんですが、そんなわけにもいかないので、後できちんと対応しようと思います。

eventportの存在を教えて貰ったので、とりあえずそれで対応してみよう。

 

今週はFFCC EOTの発売日なのでちょっと手が出せないかもしれませんが(´・ω・`)
WiiとDS両方ぽちってますよヽ( `・ω・´)ノ

Tokyo TyrantをSolaris10で使えるようにする 実装編その1

# TokyoとTyrant(Cabinet)の間はスペース入れるのが推奨みたいです(´・д・`)

それはさておき、

とりあえず動きましたヽ( `・ω・´)ノ

/dev/pollのサンプルが全然なかったので、Sunのページとlibeventを参考にしました。
(sun) http://docs.sun.com/app/docs/doc/816-5177/poll-7d?l=pt_br&a=view
(libevent) http://monkey.org/~provos/libevent/
(libev) http://software.schmorp.de/pkg/libev.html

今更だけどlibevも見れば良かった・・。
簡単なechoサーバのサンプルがあると嬉しかったのですが、見つけられなかった。゚(゚´Д`゚)゚。

とりあえず環境的なところで詰まったのは、/usr/ccs/bin/ldだと-soname指定が上手く動かない事。--with-ldとか指定してもダメだったので、とりあえず以下のページを参考にして/usr/local/bin/ldにシンボリックリンクを張りました。
http://www.rinneza.com/junya/tech/solaris10_x86/network/samba_3.2.4.html

あとはTokyo Cabinet同様にconfigure中のstd=c99をstd=gnu99に変更。

今後やることは、

今は最初に確保した配列を全部/dev/pollのハンドラに書き込んでいるので、これを必要な分だけ登録する。(配列[fd].xxxxって処理してるのを0,1,2,と使うように変更する。)
でもどっちが良いのかな・・。 ゥ─σ(・´ω・`*)─ン…

あとは↑の配列の確保をcallocで処理しているので、終了時にメモリ解放のコードを入れてあげる。
(終了時だから解放しなくても良いものなのかな・・? ってダメだよね・・ (´・д・`))

なんでcalloc使っているかと言うと、以下のようなコードで配列の添え字を求めているからです。

  int nfiles = 32000;
  struct rlimit rl;
  if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
      rl.rlim_cur != RLIM_INFINITY)
    nfiles = rl.rlim_cur;

もうちょっと負荷かけたりスレッド周りの検証したらコードも公開します。
追加したコードはfprintfのデバッグコードも含めて100行くらいでした。

 

追記 2009/1/25 23:00

複数接続でIOがブロックされちゃうのと、切断時に何回もCLOSEを試みて↓のエラーが数秒出続けてしまう。その間は新規接続も受け付けてくれない・・。
2009-01-25T23:24:24+09:00       close failed
2009-01-25T23:24:24+09:00       connection finished
・・・

 

Tokyo TyrantをSolaris10で使えるようにする 方針編

Tokyo TyrantをSolaris10に対応させる方法として考えられる方針は以下になります。

  • epoll使っている部分の実装を書き換えちゃう
  • libeventを使う
  • epoll_create, epoll_ctl, epoll_waitをエミュレートする

ソースを見るとkqueueの場合はepollのエミュレートをしていてepollに寄せているみたいなので、とりあえず/dev/pollの実装も寄せる方向で実装してみようと思います。(libeventの使い方を覚えるのが面倒という理由もちょっと有り)

それにしてもSolarisユーザーほんと少ないですね(´・ω・`)
情報が少ない(´∩ω∩`)

Solaris10でTokyo Cabinetをビルドしてみた

Tokyo Cabinetってなんぞ?という人は、
http://tokyocabinet.sourceforge.net/
このページを。

Tokyo TyrantをSolaris10で動かすために、まずはTokyo Cabinetをビルドしようとしたらいろいろと怒られたので無理矢理ビルドを通してみました。正しい修正かどうかは知りません(*・ε・*)
なので指摘は大歓迎です。

環境は「SunOS tamsvr10 5.10 Generic_127128-11 i86pc i386 i86pc Solaris」です。
あとgccやccが混在していたり、いろいろ弄っていた環境なので実は素のSolarisだとすんなり通っちゃうのかもしれません。

アーカイブをげっちゅ。

wget http://tokyocabinet.sourceforge.net/tokyocabinet-1.4.1.tar.gz
gtar zxvf tokyocabinet-1.4.1.tar.gz
cd tokyocabinet-1.4.1

まずはconfigureを弄ります。これはrealpathが云々と怒られる対策です。std=c99をstd=gnu99にします。特に外から設定を与えない場合は1カ所の修正で大丈夫ですが、fastestの場合とか中で結構分岐していたので弄くり倒す予定の人は一括置換してください。

 MYCFLAGS="-std=gnu99 -Wall -fPIC -fsigned-char -O2"

Solaris10では「pthread_yield()なんて実装は排除しちゃったよ。あなたたちは違うんです。フフンっ。」という事らしいので、その代わりに「sched_yield」を使います。
本当は「_POSIX_PRIORITY_SCHEDULING」が定義されているか確認して、定義されていれば↓を、そうでなければ別の代替実装をするのが正しいのかもしれません。

// myconfig.hの413行目くらい
#if defined(_SYS_SUNOS_) && TCUSEPTHREAD
#include <sched.h>
#define pthread_yield() sched_yield()
#endif

警告が気持ち悪いのでtcutils.cを弄ります。

// tcutils.c
//static pthread_once_t tcglobalmutexonce = PTHREAD_ONCE_INIT;
static pthread_once_t tcglobalmutexonce = {PTHREAD_ONCE_INIT};

あとはconfigureしてgmakeします。
-D_REENTRANTはgmtime_r, localtime_rの実装を有効にするために、LIBS="-lrt"はsched_yield関連で必要です。

./configure CC=gcc CFLAGS="-D_REENTRANT" LIBS="-lrt"
gmake

まだ動くかどうか確認していません(; ・`д・´)
これで動いたら次はTokyoTyrantの/dev/poll対応してみます。

※追記:動きました。

焼き鳥が食べたいので

都内で比較的美味しい焼き鳥のお店を募集!

知っている人は私のメッセ(かコメント)までお願いします(*・ε・*)

 

私の舌を満足させてくれた方にはもれなく1名様に↓のピザを送りつけます(・∀・)
http://www.pizzahut.jp/whatsnew/102_090109_ffcc/

 

PCサーバを買いました

とうとうPCサーバを買ってしまいました。
Q9550sが発売延期な上に値段が結構高かったのと、Q9650が大幅値下げ(値段が1個下にシフト)したのが理由です。

秋葉原まで行ったのですが、迷子になりました。(´Д⊂グスン
行きたいお店にたどり着けない。

あ、ここ安い!
→でも違うお店はどうかな~。。
→向こうのが安かったなぁー・・
→あれ・・さっきのお店どこ。・゚・(ノд`)・゚・。

というわけで、構成です。

CPU: Intel Core2 Quad Q9650 \31,980
MB: Gigabyte GA-EG45M-DS2H \13,980
MEM: メーカー品のやつ 2GB*2 *2 \4,980 * 2
HDD: WDの1TBのやつ \7,980
DVD-RW: \2,980
電源: サイズ CorePower500W \5,980

合計: \72,860

年末はメモリが安かったのに今は高いですね。
メモリがちょっと予算オーバーのため7万円超えちゃった。。
あと電源が一番重たかった (;´Д`)

BIOSのアップデートをしたいので最初にWINDOWSを入れました。ついでなので軽くベンチマーク。本当はFFベンチとか走らせたいところですが・・。

次に今使っているノートパソコンのベンチマーク。

Σ(´Д`lll)エエ!!
これをサーバにするのもったいない気がしてきた。

と言いつつFedora10(x86_64)をインストール中。
会社はSolarisだしSolarisでコンテナを使えばいろいろ実験が出来そうだなぁと思いつつ、やっぱり便利なLinuxへ。

エビチリ

作ってみた。

作ってから気がついた。

エビあんまり好きじゃない(;´Д`)

反省

使いたければ調べてねって言うのは横暴だなぁと思った。反省。

調べてサンプル作っておこう。

 

行値構成子(行値式)

今日はまってしまった行値式について。

行値式とはこんな感じのやつです。

WHERE (XXX,YYY) IN ((1,2),(3,4))

何ではまったかと言うと、INの後の()を忘れて IN  (1,2),(3,4)と書いていました。恥ずかしい(´・ω・`)

製品によっては行値式をサポートしていないらしいので、そういう場合は以下のように無理矢理書いちゃいます。(必ずしもこれがベストってわけではなく、EXISTSつかったり他の方法使った方がたぶん幸せになれます)

WHERE ((XXX = 1 AND YYY = 2) OR (XXX = 3 AND YYY = 4))

 

Subversionの設定メモ

普通にSubversionを設定するだけなら簡単ですが、Rootを持っていないサーバに対して設置して貰う場合は依頼内容を明確にしないといけないのでそのメモ。

○前提条件

  • Apacheと連携してhttp://でアクセスできるようにする
  • 1個の設定で複数のレポジトリを管理できるように
  • Apacheのユーザーはnobodyで動いている
  • SVNのデータ置き場は/data/svn
  • BASIC認証をする
  • 特定のディレクトリproj1/document は自動管理をする。 

○Apacheの設定

<Location /svn>
    DAV svn
    SVNParentPath /data/svn
    SVNListParentPath on

    AuthType Basic
    AuthName "tamtam private repository"
    AuthUserFile /etc/svn-auth-file
    Require valid-user
</Location>
<Location /svn/proj1/document>
    SVNAutoversioning on
</Location>

HTTPSのみ受け付けるようにするには、以下の記述を追加する。

    Require SSL connection for password protection.
    SSLRequireSSL

○レポジトリを作成する

svnadmin create /data/svn/proj1
chmod -R nobody:nobody /data/svn/proj1

○コミットメールの設定

コミットメールはhookディレクトリにpost-commitというファイルを作成します。
検索するとRubyを使ってメールを送信しているものが多いですが、会社のサーバには標準インストールされていないのでPERLで書いてみます。

これは別の記事に書こう。

Subversionのワークディレクトリのリビジョンを取得する

以下のコマンドでリビジョンを取得できます。

svnversion -n ディレクトリ

指定するディレクトリは.svnディレクトリが存在しなければいけません。
-nオプションをつけると末尾の改行を出力しません。

この機能を使って今回はビルドバージョンを埋め込むようにしました。具体的なコードは面倒なので書きませんが、Antで以下の手順で処理します。

・SVNからソースを取得
・リビジョン番号を取得する
・コンパイルする→DESTディレクトリへ
・必要なファイルをDESTへコピーする
・DESTにあるXMLファイルへリビジョンを埋め込む
・WARファイルを作成する
・サーバへデプロイする

こんな感じかな。正確にはXMLの設定ファイルに「svn.revesion」みたいな定数を定義して、その値を置換しています。(このXMLファイルの設定は実行時にDIで設定保持クラスにInjectしています)

バイナリに埋め込む場合はコンパイルする前に埋め込まないといけないですね。

 

とりあえずは、これで共有環境のアプリがどのバージョンでコンパイルされたものなのか分かるようになりました。

イトケンのトーク&ミニライブ

イトケンのトーク&ミニライブが2月21日にありまして、、さっそくチケットを予約しようと思ったら・・・

売り切れ(´・ω・)

どゆこと!(`o´)

当日券を求めるしか無いのかなぁ・・。

 

詳細はこちら。

http://www.harmonics.co.jp/project/index.html

 

FFCC EOT

今月末発売です。楽しみです。
正直な所、FF13やDQ9よりも楽しみです!!

だからこそ・・・ だからこそダンジョン数は増やして欲しい・・・!
あと店屋のメニューが重かったのでメニュープログラマーの人がんばって軽くしてください。

+   +
  ∧_∧  +
 (0゜・∀・)   ワクワクテカテカ
 (0゜∪ ∪ +        
 と__)__) +

公式ブログがいろいろと更新されています。

こんな情報もー!
(※無断転載です。ごめんなさい。(´・ω・)

Wii版もDS版もポチりました。早く発売しないかなー。

あけおめ!

ちょっと遅くなりましたが、、

あけましておめでとうございます。

今年もよろしくお願いします。

((。´・ω・)。´_ _))ペコ

 

Home > 日記 > 日記2009前期 Archive

Search
Feeds

Return to page top