repcached

2008年04月11日

repcached-2.0リリースのお知らせと、超簡単なサンプルコード

はてなブックマークに登録

repcached-2.0(memcached-1.2.5ベース) をリリースしましたのでお知らせします。

今回の目玉はマルチマスタ構成のサポートです。
以前のバージョンはマスタ/スレーブ構成だったので、必ずマスタへ書き込まなければいけませんでした。そのため、接続先のサーバがマスタなのかどうかをクライアントが判別しなければいけなかったり、keepalivedなどと併用するなどの工夫が必要でしたが、今回のバージョンではその必要がなくなります。両方のサーバに対してデータを書き込むことができるようになったので、かなり使いやすくなったと感じています。

repcachedはパフォーマンスを最重視しているため、レプリケーションは非同期で処理しています。したがって、setと同時にレプリケーションが完了する保証はありません。つまり、サーバAにsetした直後にサーバBからgetした場合、正しい値が返ってこない可能性があります。

しかし、memcachedのクライアントライブラリは負荷分散機能を持っていて、複数のサーバがある場合、キーのハッシュ値を元にして接続先のサーバが選択されます。そのため、同一のキーに対する操作は必ず同じサーバで処理されます。これを図にすると以下のような感じになります。

rep.png

つまり、クライアントライブラリの負荷分散機能を利用すると、データを格納したサーバと同じサーバが参照されるので、非同期処理に起因するデータの不整合は発生しません。そして、すべてのデータは両方のサーバへ格納されているので、片方のサーバがダウンしてもすべてのデータが保持されます。

down.png

ただし、参照しようとしたサーバがダウンしていた時に、自動的にもう片方のサーバへ参照しにいくかどうかは、クライアントライブラリの実装に依存します。例えば、PHP の memcached extension で提供される Memcache::set 関数の場合は、Version3.0.0以上ならば自動的に再接続してくれますが、Version2.2.3ではエラーになってしまいます。そのような場合は、自分で再試行するなどの処理を実装する必要があるかもしれません。セッションハンドラ(session.save_handler="memcache")を利用する場合は、バージョン2.2.3でも再接続してくれます。
※この例は後ほどご紹介します。

ダウンしていたサーバが復旧して最初にやることは、既存のサーバの全てのデータを複製(以下、まるごとこぴー)することです。その間、復旧中のサーバはクライアントからの接続を受け付けないので、誤って中途半端な状態のデータを取得してしまうことはありません。また、まることこぴーの最中でも、コピー元のサーバに対しては正常にアクセスできるので、サービスが停止してしまう心配もありません。

marugoto.png

まるごとこぴーが完了次第、レプリケーションを再開し、両方のサーバへアクセスできるようになります。まるごとこぴーの所要時間は、ギガビットイーサの環境で10万件で3秒程度、100万件で30秒程度でした。

簡単な使用例


 簡単な使用例として以下のコードを紹介します。セッション変数を利用して、リロードするたびにカウンタが増えていくPHPです。 セッションハンドラとしてmemcacheを使い、データの保存先にrcd1とrcd2というサーバを指定しています。rcd1とrcd2ではrepcachedを起動しておきます。

[count.php]
<?PHP
$rcd1="tcp://rcd1?persistent=1&weight=1&timeout=1&retry_interval=15";
$rcd2="tcp://rcd2?persistent=1&weight=1&timeout=1&retry_interval=15";
$session_save_path  = "$rcd1,$rcd2";
ini_set('session.save_handler', 'memcache');
ini_set('session.save_path', $session_save_path);
session_start();
if(empty($_SESSION['count'])){
   $_SESSION['count']=1;
} else {
   $_SESSION['count']++;
}
echo "count=".$_SESSION['count']."\n";
?>
※PHP-5.2.5 + memcache-2.2.3で動作確認

ブラウザでこのページへアクセスすると、リロードする度にカウンタが増えていきます。 rcd1,rcd2のどちらかが動いている限りカウンタは増え続け、両方停止するとリセットします。

このように、repcached-2.0はmemcachedを2台で負荷分散するのと同じ感覚で利用できます。 すでにmemcachedを2台構成で運用しているシステムであれば、repcached-2.0に置き換えるだけで冗長構成になるかもしれません。 さすがにオリジナルのmemcachedと比べると若干パフォーマンスは落ちますが、それでも実用には支障のないレベルに仕上がっていると思いますので、もしよろしければご利用下さいませ。

klab_gijutsu2 at 07:00|この記事のURLComments(0)TrackBack(1)
2008年03月12日

repcached-1.2をリリースしました

はてなブックマークに登録

repcached-1.2 (memcached-1.2.4ベース) を公開しましたのでお知らせします。

今回のバージョンアップは以下の2件の不具合修正になります。

  • flush_allのレプリケーション時にマスタでメモリリークする可能性があったのを修正しました
  • flagsパラメータがレプリケーションされていなかったのを修正しました

ダウンロードやインストール方法などの詳しい情報は、プロジェクトページをご覧ください


flagsパラメータがレプリケーションされない不具合の詳細


memcachedプロトコルでは更新系コマンドにflagsというパラメータがあります。
set key 0 0 5
        ↑これ
本来であれば、このパラメータもレプリケーションすべきですが、以前のバージョンではバックアップで常に0をセットしてしまっていました。
master: set key 1 0 5  ← Masterはflags=1なのに
backup: set key 0 0 5  ← backupはflags=0になっちゃってる

flagsパラメータを利用していないアプリケーションには影響ありませんが、PHPのMemcache::set関数などを使ってオブジェクトを格納しているようなアプリケーションでは致命的な問題となります。例えば、以下のようなコードです。
※環境は php-5.2.5 + memcache-2.2.3.tgz です。

【ソースコード】
#!/usr/bin/php
<?PHP
$memcacheM = new Memcache;
$memcacheB = new Memcache;
$memcacheM->connect('master', 11211) or die ("connect errorM\n");
$memcacheB->connect('backup', 11211) or die ("connect errorB\n");
$tmp_object = new stdClass;
$tmp_object->str_attr = 'test';
$tmp_object->int_attr = 123;
$memcacheM->set('key', $tmp_object, false, 10) or die ("set error\n");

$get_resultM = $memcacheM->get('key');
echo "Master\n";
var_dump($get_resultM);
echo "\n";

$get_resultB = $memcacheB->get('key');
echo "Backup\n";
var_dump($get_resultB);
?>

実行結果は以下のようになります。

【実行結果】
 
Master
object(stdClass)#5 (2) {
  ["str_attr"]=>
  string(4) "test"
  ["int_attr"]=>
  int(123)
}
 
Backup
string(66) "O:8:"stdClass":2:{s:8:"str_attr";s:4:"test";s:8:"int_attr";i:123;}"

なんと、Masterからgetした場合はオブジェクトとして扱われているのに対し、Backupでgetすると文字列として扱われてしまっています。PHPのMemcache関数は、データをシリアライズして格納しているかどうかを判断するためにflagsを利用しているようです。もしかすると他のライブラリでも同様の問題があるかもしれませんのでご注意下さい。


repcached-2.0の近況


現在、memcached-1.2.5をベースにしたrepcached-2.0を開発中です。実装のほうはほとんど終わっていまして、今はコードを見直しながらストレステストの準備をしています。早ければ今月中、遅くとも来月の頭にはリリースできると思いますのでご期待下さいませ!



(や)

klab_gijutsu2 at 03:12|この記事のURLComments(0)TrackBack(0)
2008年02月18日

repcached 1.1をリリースしました+2.0のもくろみ

はてなブックマークに登録

repcached 1.1 (memcached-1.2.4ベース) を公開しました!

主な変更点は次の通りです。

  • memcached-1.2.4の新しいプロトコル (append, prepend, cas) に対応。
    • ただし、いまのところcas unique IDのレプリケーションには対応していないので、getsとcasの間にフェイルオーバが発生すると、casが失敗する可能性があります。
  • レプリケーション用のテストケースを同梱しました。prove t.repでテストを実行できます。

ダウンロードやインストール方法などの詳しい情報は、プロジェクトページをご覧くださいませ!

そして2.0

1.x系で実装したいと考えていたことが完了し一段落したので、ぼちぼち2.0に向けて動き出しています。

repcached 2.0での目玉機能は、2台でのマルチマスタ構成のサポートです。
このマルチマスタが実装されると、こんないいことがあります:

  • クライアントはサーバのフェイルオーバ、フェイルバックを意識する必要がなくなります。 (※クライアントライブラリに依存します)
  • 2台のサーバを有効に活用することができます。
    repcached 1.xでは、バックアップ機が遊んでいましたが、2.xではちゃんと仕事をしてもらいます。

マルチマスタを実現するアイディアについては、 去年行った社内勉強会『repcachedのお披露目』の資料 (PDF)を見ていただければと思います。

ただいま、鋭意実装中なのですが、 みなさんに公開できるのはもう少し先 (3月ぐらいですかねー) になると思います。
リリースの準備ができましたら、またここでアナウンスしますのでご期待くださいませ!


(ひ)

klab_gijutsu2 at 07:00|この記事のURLComments(2)TrackBack(0)
2007年11月15日

repcached 1.0リリースの補足とちょっとしたHowTo

はてなブックマークに登録

前のエントリでrepcached 1.0のリリースをお知らせしました。
開発の動機はプロジェクトページにちょっと書いたのですが、たいせつなことをひとつ書き忘れていました。

repcachedを開発しようと思い立ったのは、ひとことでいうと「キャッシュサーバといっても止まらないようにしたい」、別ないい方をすると「サーバ管理者だって夜はぐっすり眠りたい」というものでした。
そしてふたりのエンジニアが自ら社内向けに企画・提案してrepcachedの設計・開発にとりかかり、プロダクション環境で使えるようなレベルのものができあがりました。

これがrepcachedを開発した動機です。

さて、昨日はrepcachedをみなさんに向けて公開しました。
いうまでもなく、repcachedを社内に閉じて外部には公開せずに使い続けるという選択肢もありました。
でもわたしたちはそうしませんでした。

わたしたちはいろいろなオープンソースのプロダクトのお世話になって、今日もおいしいごはんが食べられる生活を過ごせています。とても幸せなことだと思っています。
でも、常々こうも思っていました。
「give and takeでいえば、takeしてばかりではないか?」
「どうしたらgiveすることができるだろうか?」
そして、
「いつかはみなに使ってもらえるようなオープンソースソフトウエアを作りたい」
という想いをもっていました。

これが前のエントリで書き忘れたこと、そしてこのエントリで(ちょっとはずかしいですがw)みなさんに伝えたかったこと ―repcachedを公開した動機― です。

ちなみにrepcached以外にも、いくつか公開しているソフトウエアがあるのですが、これらもすべて同じ想いで公開しているものですのでお使いいただければと思います!


だいぶ前置きが長くなってしまいましたが、後半は、各所でいただいた意見を反映しながら、使い方についてちょっと補足したいと想います。

repcachedのビルド

現時点でのrepcachedのバージョンは1.0、対応するmemcachedのバージョンは1.2.2です。以降の説明では$MCDVER$RCDVERというシェル変数を使っていますが、これらには次のようにバージョン番号を格納している

$ MCDVER=1.2.2
$ RCDVER=1.0

とします。もし、将来、バージョンが変わっても、新しいバージョンを代入すれば以降の手順はそのまま使えると思います。

repcachedはmemcachedに対するパッチという形式で配布していますので、memcachedのアーカイブを入手・展開して、repcachedパッチをダウンロードして適用してください。

$ wget http://www.danga.com/memcached/dist/memcached-${MCDVER}.tar.gz
$ tar zxf memcached-${MCDVER}.tar.gz
$ cd memcached-${MCDVER}
$ wget http://downloads.sourceforge.net/repcached/repcached-${RCDVER}-${MCDVER}.patch.gz
$ gzip -cd repcached-${RCDVER}-${MCDVER}.patch.gz | patch -p1

また、memcachedにrepcachedパッチ適用した状態のアーカイブも配布していますので、こちらを利用になっても構いません。

$ wget http://downloads.sourceforge.net/repcached/memcached-${MCDVER}-repcached-${RCDVER}.tar.gz
$ tar zxf memcached-${MCDVER}-repcached-${RCDVER}.tar.gz
$ cd memcached-${MCDVER}-repcached-${RCDVER}

ソースコードの準備ができたら、つづいてコンパイルです。
レプリケーション機能を有効にするには、configureで--enable-replicationを指定します。ただし、--enable-replicationと、memcachedでスレッドサポートするための--enable-threadsは併用できない点に注意してください。

スレッドサポートと併用できない理由(ちょっと内部実装の話になりますが)はこうです。repcachedの場合、レプリケーション待ちのデータを内部で保持するためにキューを作って使っているのですが、スレッドサポートの場合は複数のスレッドが同時にこのひとつのキューに対して読み書きをするため、排他制御をかける必要があります。実は当初はスレッドサポートで排他制御をするように実装していたのですが、性能評価をした結果、思った以上に性能が低下したので、思いきってスレッドサポートをやめて、一切、排他制御が必要のない実装にすることにした、という経緯があります。

さて、そのほかのconfigureのオプションはオリジナルのmemcachedと同じです。 configureが成功したら、続いてmake、make installを行います。

$ ./configure --enable-replication
$ make
# make install

起動

インストールができたら起動してみましょう。

repcachedでは2つのオプションが追加されています。

-x <ip_addr>
マスタのホスト名かIPアドレスを指定します
-X <port>
レプリケーション用のポートを指定します。デフォルトは11212です。 理由がなければオプション無指定でデフォルト値でいいと思います。

-xオプションでマスタを指定します。
このオプションが指定された場合は、まず、指定されたマスタ(の-Xオプションで指定されたポート。デフォルトで11212)に接続しにいきます。
もし、接続できた場合は、自分はバックアップ機として動作します。
一方、接続できなかった(=マスタが存在しなかった)場合は、自分はマスタ機として動作をするようになっています。

では具体的に、2つのホスト、fooとbarでレプリケーションしてみましょう。

まずは、fooでmemcachedを起動します。 (repcachedパッチを適用しても、memcachedプログラムのファイル名は「memcached」のままです)
マスタとしてbarを指定(-x bar)していますが、この時点ではまだホストbarではmemcachedが起動していない(=マスタが存在しない)ので、fooはマスタ機として動作します。

foo$ memcached -v -x bar
replication: master start

続いてホストbarで、マスタとしてfooを指定(-x foo)してmemcachedを起動します。
さきほど、fooではマスタとなるmemcachedを起動していますので、barのmemcachedはマスタの存在を検知してバックアップ機として振る舞います。

bar$ memcached -v -x foo
replication: backup start (master=10.10.2.26:11212)
replication: connect

うまくレプリケーション接続が確立できた場合、起動オプションに-vをつけていればマスタ側に「replication: accept」と出力されるはずです。

レプリケーションの確認

この状態で、正しくデータのレプリケーションが行われているはずです。試しに、マスタ(foo)に適当な値をset(例えば、key:test1, data:one)してみて、同じ値がマスタ機(foo)とバックアップ機(bar)の両方から得られるか確認してみてください。

repcachedは、シングルマスタ/シングルバックアップという構成でレプリケーションします。
現時点では、マルチマスタや、シングルマスタ/マルチバックアップという構成では動作しませんので注意してください。

フェイルオーバとフェイルバック

レプリケーションの動作が確認できたら、repcachedのもうひとつの目玉機能であるフェイルオーバを試してみましょう。

マスタ(foo)、バックアップ(bar)でレプリケーションができている状態で、おもむろにマスタ(foo)のmemcachedを終了してみます。

するとバックアップだったbarは、マスタの不在を検知して、自らがマスタとして振る舞うようになります。barのターミナルには次のように表示されているはずです。

replication: close
replication: master start

つまり、バックアップ機がマスタに昇格してフェイルオーバが成功したわけです。
注目して欲しいのは、昇格する際にbarに対して外部から管理コマンドを送ったり通知シグナルを送ったりする必要はなく、自動的に昇格したという点です。

さて、いまの状態を確認しておくと、マスタ(bar)だけでバックアップ機はなしという状態です。
あとの実験のため、ここで適当な値(例えば、key:test2, data:two)をマスタに対してsetしておきましょう。

ところで、いつまでもマスタ1台だけの片肺飛行はまずいので、バックアップ機を復帰(フェイルバック)してみることにしましょう。

ホストfooで、次のようにしてmemcachedを起動します。

foo$ memcached -v -x bar
replication: backup start (master=10.10.2.27:11212)
replication: connect

ここで注目してほしいのは、いちばん最初にfooでmemcachedを起動したときと、オプションが全く同じだということです。
いちばん最初の例では指定マスタが存在しなかったので自らがマスタになりましたが、こんどは指定されたマスタ(bar)が存在するので、バックアップとして動作します。
繰り返しになりますが、repcachedでは、状況(マスタが存在するか/しないか)に応じて、適切な振る舞い(マスタになるか/バックアップになるか)を自動的に判断するように実装しています。

これで再び、マスタ/バックアップ構成に復帰できたわけですが、双方が保持するデータはどのようになっているでしょうか?
試しに、最初にレプリケーションできたときにsetした値(key:test1)と、マスタだけの片肺飛行中にsetした値(key:test2)を、バックアップからgetして値を確認してみてください。マスタと同じ値がバックアップ機からも得られるはずです。

実は、ひとつもデータを持っていない新しいバックアップ(foo)がマスタ(bar)に接続した場合、まず最初にマスタが持っているすべてのデータをバックアップにコピーするようにしています。ですので、まっさらなバックアップ機を復帰した場合でも、マスタとバックアップとで保持しているデータが食い違ってしまうといったことは起こりません。
ちなみに、手元の環境(1000Base-T)でマスタ→バックアップの全コピーにかかった時間は、20万件で2秒、100万件で10秒でした。

おちぼひろい

フェイルオーバ時のクライアントの対応

repcachedはシングルマスタ構成なので、memcachedのクライアントは接続するサーバとしてただひとつのIPアドレスを指定する必要があります。
しかし、repcachedがフェイルオーバした場合、マスタの役割を果たすサーバが変わってしまうので、サーバのIPアドレスも変わってしまいます。
これではフェイルオーバのたびにクライアントの設定を変えなくてはならず、「ラクな運用」を信条とするDSASでは受け入れられません。
この問題については、後日、別エントリでスマートな解決方法を考えてみたいと思っていますのでご期待くださいませ。

パフォーマンスについて

レプリケーションという機能を追加している以上、オリジナルのmemcachedとrepcachedはまったく同じ性能である、というのは嘘になります。
しかし、オリジナルのmemcachedがもつイベントループやIO多重化の機構にうまくとけこむようにrepcachedの処理を実装したので、性能の低下は「ないわけではないがそれほど大きい低下ではない」と考えています。
気になる方は、要求や用途に即した環境においてご自身の手で性能評価を行うのがいちばんだと思います。
が、参考までに記しておきますと、わたしたちが行ったマイクロベンチの結果はプロジェクトページの方に書いてある通りで、ベンチマークプログラムに改善の余地はあるものの、秒あたりのset数、秒あたりのget数共に高い数値ではないかと思っています。

klab_gijutsu2 at 02:07|この記事のURLComments(0)TrackBack(2)
2007年11月14日

repcached 1.0をリリースしました!

はてなブックマークに登録

repcached初の公開バージョンとなる1.0を公開しました。

repcachedとは、memcachedに以下の機能を追加するプロダクトです。

  • アクティブ/バックアップ構成での自動的なフェイルオーバ
  • 無停止のフェイルバック

「キャッシュサーバなのに冗長構成って必要なの?」と思うかもしれませんが、そのあたりの開発動機も含め、インストール方法などのドキュメントは上記のプロジェクトページにありますのでご覧くださいませ!

追々、このブログでも活用事例を紹介していきたいと思っています。

klab_gijutsu2 at 16:33|この記事のURLComments(0)TrackBack(0)
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント