中編から数ヶ月が経過してしまいました^^;
今までなにをやっていたかと言うと、「既存プログラムの改修」作業です。
まだ完全には終了していませんが、覚書として「プログラム対応編」をお送りします。
もともと書かれていたコードがタコだったり用途が限定されたりしてあまり参考にはならないかもしれませんが・・・。
さて、なぜ既存プログラムの改修が必要か、ということなんですが、最大の原因は
LOB(ラージオブジェクト)を使用しているサブシステムがある
これに尽きます。
LOBについての詳細は他に詳しく解説されているページがあるはずです(他力本願)ので割愛しますが、レプリケーションミドルウェアとして選定したpgpool-IIの制限事項として、
乱数やトランザクションID,OID,SERIAL, シーケンス,CURRENT_TIMETSTAMPのようなものに関してはレプリケーショ ンはしますが,2台のホストでまったく同じ値がコピーされる保証はありません
とあり、LOBはもともと扱いが特殊な上OIDを直接使用する形になりますので、pgpool-IIを経由して使うことはできません。
というか pg_lo_* 関数を実行した時点でpgpool-IIから怒られます^^;
ということで、まずはLOBを使用しないようシステムを改修する必要があります。
他にもOIDやシーケンスなど、制限事項に触れそうな機能を利用しているプログラムを洗い出し、必要に応じて修正していく必要があります。
チェックすべき点として以下の4つを想定しました。
- 既存のデータベースアクセスライブラリを使用せず、直接pg_*関数を利用してアクセスしているか
- 発行しているクエリに危険そうなもの(シーケンスやnow()等)は利用しているか
- LOBを使用しているか
- OIDを利用しているか
ざっとプログラムファイルを洗い出してみました。その数、約1500。気が遠くなりますね<丶´Д`>げっそり
全てのファイルをチェックするだけで1ヵ月半以上かかってしまいました。この作業だけに専念できるわけではなく、随時他の作業や障害対応などの割り込みが発生しますから、仕方ないっちゃー仕方ないかもしれませんが。。。ちょっと引っ張りすぎちゃったかな。
結論として、
- LOBを利用しているシステムは他の格納手段に変更する。
- now()等日付・タイムスタンプ関連はこの際ほっとく。後々はライブラリで置換処理を行う等、対応を取るかも。しかし、ちょっとしたタイムスタンプの違いでpgpool-IIに異常発生と判断されても困るので、pgpool-IIのオプション”replication_stop_on_mismatch”はFALSEで運用することとします。
- 直接アクセスについても今回は危険なクエリが見つからなかったため後回し。
ということになりました。
最大の難関「LOB」ですが、今回対象となったサブシステムでは画像データの格納に使用されていました。もともと自分で書く時はLOBを使わず
- データそのものはファイルに
- ファイルパス(URL)などの情報を保持するインデクステーブルをDBに
というスタンスだったのですが、、、このシステムは外部委託して開発したものでした(T-T*)
愚痴っていても仕方ないので他の方法を考えます。さくっと思いついたのはこの3つ。
- 今までの方法(データはファイル、インデクスをDB)
- 画像データをエンコードしDBに突っ込む
- HashDB等、全く別のDBシステムを使う
まず(1)ですが、方法としては今まで行ってきたことなので、仕組みそのものは枯れています。が、対象システムのコードを調査した結果、かなりの修正量になってしまう恐れが出てきました。
となると(2)か(3)・・ということになります。
そんな中、「Web屋のネタ帳」さんで見つけたこんなエントリ。
画像もDBに格納して管理する -扱いがめんどうなLOB(ラージオブジェクト)は使わない方法も含め
決まった。(2)で行きましょう。ライブラリを作る手間はかかりますが、一度作ってしまえば他システムでも応用できますしね。
あ、そうだ
「だったらストレージをRDBMSに固定しなくてもいいんじゃね?」
ということで考えを推し進めた結果、こんなことになってしまいました。
- 「キー」と「値」だけのシンプルなデータベース(仮にHashDB)を想定する
- ストレージとして通常のPostgreSQL等RDBMSと、Memcachedを選択できるようにする
- RDBMSストレージの場合、渡されたデータをtext型カラムに格納する。必要に応じMIMEエンコードとシリアライズを行う
- Memcachedストレージの場合はそのままAPIにお任せ
早速開発。
基本的なハンドリングAPIはmemcachedのものを踏襲することにします。だってHashだもん。
- コンストラクタ
- connect / close / isConnected
- add
- set
- get
- replace
- delete
- flush
- increment / decrement
これだけあれば充分かと(increment/decrementが本当に必要なのかは置いといて)。
- インスタンス生成時もしくは setOption メソッドでストレージドライバの種類やドライバが必要とするDSN等を渡し、
- connect メソッドでストレージに接続
- add/get/set/replace/delete でキー+データペアを弄び
- close メソッドで切断
てな感じを想定しています。あくまでHashDBを実現するためだけのシンプルなクラスライブラリですね。
さて、実際にストレージへアクセスするドライバにもそれぞれ上記のメソッドを実装していきます。HashDB_Storage_Common という抽象クラスを作り、これを継承しサポート可能なメソッドを実装していく感じですね。
memcached についてはAPIそのまんまですからあまり問題はありません。
PostgreSQL版のほうですが、渡されたデータがバイナリだった場合MIMEエンコードが必要です。
しかし、時間があまり無いなど諸々の事情で「問答無用のMIMEエンコード+シリアライズ」となりました。後々のことをちょっとだけ考え、ドライバそのものにエンコードやシリアライズ処理を載せるのではなく、もう一つ「データコンテナ」クラスを作り、そちらでエンコード/デコード、シリアライズ/アンシリアライズの面倒を見させることに。
さて、ここで一つ問題が。
単純にアプリケーションから渡されたキーをそのままHashキーとしてしまうと、他のアプリケーションやシステムが使用するキーとバッティングしてしまう恐れがあります。また、「このアプリで使っているHashデータだけを削除したい!」ということがありうるので、ベタキーはちょっと問題がありそうです。
PHPのセッションハンドラとしてmemcacheを使うとか、このライブラリを通さずに使うこともありますしね。
ということで、
- HashDBパッケージとしてのプレフィクス
- アプリケーション/サブシステムとしてのプレフィクス
の2段階プレフィクスをつけることとしました。
要するに、アプリケーションとしては “TESTKEY001” というキーで追加されたデータは、実際には”HASHDB_SUBSYS01_TESTKEY001“というHashキーで格納されることになります。
パッケージレベルのプレフィクスはクラス自体を継承することで、またアプリケーションレベルのプレフィクスはコンストラクタで設定することとしました。
次回、実際に画像データを格納/取得する部分の作りこみをメモりますね。
んでもって。
ものすご~~~~~~~く恥ずかしいんですが、、、、こっそりアーカイブを公開してみたりします。笑ってやってください。
アーカイブを展開すると Tools ディレクトリが作られ、その中に HashDB ライブラリができます。内部でrequireしているパスにToolsを入れてしまっているので、できればディレクトリ構成はこのままが良いかと(requireのパスを変えればいいんですが)。
あ、あと超個人的な好みにより、ファイル拡張子は .inc になっています。適宜修正しちゃってください。







1月 8th, 2009 at 19:43
クラスタリングなんぞ、全然考えてないから、シーケンスは使いまくりだw
同値のキーで複数レコード突っ込む必要があり、該当のレコードを後から、1レコードを狙い撃ちしなくちゃならんようなことになっとるしなぁ…
シーケンスで管理すると楽だと思ってしまい(^^;)
キー(同値含む)は、外(利用者)からわかる(例えば、日付)
↓
一覧を取得(一緒にシーケンスで振られた値を隠してwくっつける)
↓
どれかいらう(変更や削除など)
↓
キー(日付など)で狙い撃ちすると、他のレコードも巻き添えにwww
だから、ユニークになってるシーケンスで振られた値でレコード固定
と言うことにしちゃってるからなぁ…
根本的に考えなおさないと、クラスタリングは無理っぽい。
同時利用者が10人未満のシステムですがwww<クラスタリングなんか考えてない(^^;)
1月 16th, 2009 at 10:48
>ぐてさん
いあ~、俺もシーケンスは便利に使いまくってますよ、ほんと。
サポートされてないっていうか、同一値が返ってくる保障がないんですよ。
商用のがっつりしたクラスタシステムなら大丈夫だとは思いますが、今回は全てOSSで仕上げたので若干の制限が出てくるんですよねー。
トランザクション内でシーケンスをいじれば大丈夫なので、そういう修正が必要なんですよ、今回。
もー、アップアップです。