トップ «前の日記(2004-11-05) 最新 次の日記(2004-11-07)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2004-11-06

_ メッセージングの3番目

以前、P2PとPub/Subしか知らん。ポーリングくそと書いたけど、やっぱ、第3の道は、『常にクライアントがトリガーとなってキューにアクセスできるモデルだ。』なのか。がっかり。

#こういうことだ。大した話ではなくきわめて単純な非EJBでの手作りメッセージングの話。

今は2台だとする。この場合P2PでもPub/Subでも良いが、プッシュモデルである以上、配送元に設定が必要である。

しかしシステムが発展すると、第2、第3の配送先が必要になる可能性がある。この時、配送元は既に安定稼動しているのだから、いっさい変化を与えたくはない(H/W増強とかはもちろん別の話となる)。第2、第3の配送先にのみ、何かをすればすぐに配送元に繋がるという状態が望ましい。

つまり、ある意味において一元管理はしたくないということだ。新規に追加されたもののみ変更したい(というか設定したい)ということ。

この場合、P2Pは最初から土俵に上がることさえできない。

Pub/Subの場合はどうだろうか?

設定としてあらかじめ配送先を配送元に記述する方法は最初の要求から問題外だ。

しかし配送リクエストをネゴするという方法は取り得る。

この場合、配送元はプッシュする前に配信先のポートを知ることができるからだ。FTPみたいな。

ネゴの過程で配送デーモンを1つ相手に貼り付ける。後の動作はP2Pとして実行しても良い。この場合の問題点はめんどうだということだ。めんどうなソフトウェアはバグの混入率が大抵の場合上がってしまう。配送デーモンが消滅することの監視も必要となる。(スレッドではなくプロセスにすれば多少監視しやすい)ネットワークを使う場合、キープアライブを流すのを避けたり無通信検出タイマーの間隔が長いとこの状態が発生してしまう。(よく考えたらP2Pでも動揺だ)

(配送先が消滅したら配送元の送信エラーで発見できる)

ネゴの問題点はもう1つ、タイミングである。配送先がネゴしようとしたら配送元がたまたまメンテ中だったから無応答。この場合、配送先に適切なリトライのロジックが必要となる。これもめんどうのうちだ。

これらについてポールが解決策となりうるのはわかり切っている。

この場合の考慮点は、

1.メッセージの抜け対策

2.配送先の突然死(誰も気付かない)

3.ポールの間隔

の3点。1.は配送先毎のキューを使わないことから簡単に発生しうる。配送先が独自のシーケンス管理(キュー—と言っても1本しかないし、配送先複数が前提なので誰もデキューできない。したがって、単純にはDBMS上のテーブルとして配送元は何も考えずにインサートし配送先が勝手に読み取るというのが現実的だ)

たとえば、配送元はSEQUENCEを使ってキュー(っていうかインサート)していく。障害が発生すればSEQUNECEは飛ぶから必ずしも1繰り上がるわけではない。

そのため、配送先がSELECT DATA FROM QUEUE WHERE SEQ=?; setInt(1, current+1); というようには単純化できない。また、発生順の維持は当然必要だ。したがって

SELECT DATA,SEQ FROM QUEUE WHERE SEQ=SELECT MIN(SEQ) FROM QUEUE WHERE SEQ>?; setInt(1, current);

でなければならない。(が、これはこれだけのことだ)

このようにして得たデータ(というよりも現在のSEQの値)を配送元は独自に管理する。

2.については時々、配送先のプロセスとは異なる(配送先と同一ノード上の)プロセスが、

SELECT max(SEQ) FROM QUEUE

を実行し、配送先プロセスのログと突合せをして一致していなければ怒る(怒る前に数秒待って、配送先の受信済みSEQが変化しなければ本気で怒るとすべき。そうでなければたまたま遅延しているだけかも知れない。したがって、ここでの数秒というのは、ポールの間隔よりは長い必要がある)というような防御が必要となる。

それでも、配送元、配送先、および配送先監視のいずれのプログラムも非常に単純に実装できる。

問題は、結局、3.として残るポールによる負荷(空読みのくせに、MIN(SEQ)とかが必要となるため、必ずしも効率的ではない)だ。

#配送元のQUEUEテーブルへのインサートが複数のプロセスによりランダムに行われるとSEQUENCEのロックがあってもポールの微妙な間隔で飛ぶ可能性があることも注意点となる。

W1 W2 W3

SEQUENCE.next

insert SEQUENCE.next

insert SEQUENCE.next

insert

と必ずしもならないからだ。プロセス/スレッドスィッチがinsertの呼び出し未満で発生するとまずい(追記:バカなことを書いている。insert時点では次のシーケンスを得ているんだから全く問題ではない)。もっともSEQUENCEのカウントアップとinsertでトランザクション境界を作ってコミットするまで次のプロセス/スレッドがSEQUENCEの操作ができないようにすれば良い(かな? ちょっと微妙な感じがしないでもない)。って言うか発生順が重要なら順序維持は必須か。

やっぱりポールが固そうだな。

追記:

わかった。上のバカなことは、ラック使っている場合でパフォーマンスを上げるために各ノードがシーケンスキャッシュを使うようにさせている場合に問題となる動作を想定していたようだ。あるノードが1を得た時に100までキャッシュする。別のノードが次にシーケンスを得ると101になる。この間に最初のノードの2回目の書き込みが発生しないと配送先は現在のシーケンスを101としてしまう。これ以降、最初のノードの書き込みの2〜99は無視されることになる。

したがって、シーケンスキャッシュの分だけ現在のシーケンスからひいてやらなければ危ない。

というようなこと。

追記の追記:

だめだめだ。

最初のノードからの書き込みがずーと無いまま、2番目のノードが201まで書いてしまえば、シーケンスキャッシュ分の100を現在の配送先が得ているシーケンス(つまり201)からひいても、次の最初のノードの書き込み(シーケンス2)は得られないことになる。

固くやるには、SELECT DATA, SEQ FROM QUEUE WHERE SEQ NOT IN (自分が持ってるすべてのシーケンス) となり、それは無意味に限りなく近い。

方法1: とりあえず、SEQ > CURRENT+1 とし、時々(ポール30回に1回とか)で、WHERE SEQ < CURRENT AND SEQ > (連番の最後) とかを呼び出す。

っていうか、負荷は高まるばかりなり。

ビットマップ管理(ギャップの先頭を条件とする)とか? でもシーケンス抜けを考慮するとギャップの先頭にマージンを持たせる必要もあるわけだし。

あと、最初のノードがダウンしたら2〜99は永遠欠番になるわけだから、後の考慮がすべて無駄。

すると

方法2: SEQUENCEキャッシュを無効にする

この場合の負荷は?

まんべんなく配送元の各ノードからの書き込みがあるならば、方法1に一工夫したバージョン。配送元の書き込みが基本的に単一ノードからだったり滅多に書き込みがなければ方法2。閾値をどう得るかはえいやの世界のような。とすれば固く方法2が正解かも。処理は単純。

もっと追記:timestampを避けているのは半ば本能的なことなのだが、timestampでも良いかも。複数ノードの時刻同期ってどこまで信用できるのだろうか? Oracleのタイムスタンプ関数を使った場合ラックだと必ず一致するのかな。そのあたりが落としどころのような。

#再起動したら(前方向に)時刻がずれましたとかなるとやばめ(キーとするのだから重複したらすごくまずいというかそもそも書けない)だし。

_ とりあえず

適当にplugin/disp_referrer.rbを以下のように修正。

- 'antenna.url' => '(\/a\/|(?!.*\/diary\/)antenna[\/\.]|\/tama\/|www\.tdiary\.net\/?(i\/)?(\?|$)|links?|kitaj\.no-ip\.com\/iraira\/)',

+ 'antenna.url' => '(\/a\/|(?!.*\/diary\/)antenna[\/\.]|\/tama\/|www\.tdiary\.net\/?(i\/)?(\?|$)|links?|kitaj\.no-ip\.com\/iraira\/|i-know\.jp\/)',

tdiary.confの@referer_tableに以下の行を追加

['^http://i-know\.jp/([^/]*).*', 'i-know.jp(\1)'],

_ HPのノート

Vector(まだ迷っているっていうか、迷うだろう不要不急とは言えない状態では)。

Amazonではそんなに投売り状態ではない。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|

ジェズイットを見習え