Flareを使う(flush_allコマンドの不具合)
当ブログをご覧の皆様こんにちは。さくらインターネット研究所の大久保です。 先日、Flareにてflush_allコマンドを使用するとデータの不整合が発生するという事象が発生しました。 今回はその調査内容ついてご説明したいと思います。
不具合の内容
以前の記事ではご紹介していませんでしたが、memcachedプロトコルには保存されている全データを消去するflush_allコマンドというものがあります。Flareにおいてもflush_allコマンドはサポートされていますが、このコマンドを発行するとレプリケーションをとっている(スレーブサーバを設置している)場合に、サーバ間でデータの不整合が発生します。
開発者のグリー藤本さんにお聞きしたところ、flush_allコマンドは発行したサーバでのみ処理され、レプリケーションはされないとのことでした。そのため、あるサーバのデータは消去されるが、他のサーバのデータは存在したままとなり、問い合わせ先のサーバによってデータが存在したり、存在しなかったり、応答が変化することとなります。

この事象について以下の通り再現試験を行いました。
再現試験の内容
- テスト構成 以下の4台のサーバを用いてテストを行いました。プロキシサーバ、ストレージマスターサーバ、ストレージスレーブサーバにそれぞれflush_allコマンドを発行してどのような結果になるかを確認します。サーバ名役割IPアドレスkvs001インデックスサーバ192.168.12.151kvs002ストレージサーバ(マスター)192.168.12.152kvs003ストレージサーバ(スレーブ)192.168.12.153kvs004プロキシサーバ192.168.12.154
- 各サーバにてデーモンを起動 設定は以前と同じものを使用するため省略します。
- インデックスサーバにて役割設定 インデックスサーバにてflareiにtelnet接続して役割設定を行います。
[admin@kvs001 ~]$ telnet localhost 12120
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
node role 192.168.12.152 12121 master 1 0
OK
node role 192.168.12.153 12121 slave 1 0
OK
node role 192.168.12.153 12121 slave 1 0
OK
stats nodes
STAT 192.168.12.152:12121:role master
STAT 192.168.12.152:12121:state active
STAT 192.168.12.152:12121:partition 0
STAT 192.168.12.152:12121:balance 1
STAT 192.168.12.152:12121:thread_type 16
STAT 192.168.12.153:12121:role slave
STAT 192.168.12.153:12121:state active
STAT 192.168.12.153:12121:partition 0
STAT 192.168.12.153:12121:balance 1
STAT 192.168.12.153:12121:thread_type 17
STAT 192.168.12.154:12121:role proxy
STAT 192.168.12.154:12121:state active
STAT 192.168.12.154:12121:partition -1
STAT 192.168.12.154:12121:balance 0
STAT 192.168.12.154:12121:thread_type 18
END
quit
Connection closed by foreign host. - プロキシサーバからテストデータ投入 以下のように10万件のデータを投入します。
[admin@kvs004 ~]$ bin/memcache-set-flare.pl 0 99999
[admin@kvs004 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
VALUE test00000000 0 50
01234567890123456789012345678901234567890123456789
END - 各ストレージサーバのレコード数を確認 以下のように、マスターサーバ、スレーブサーバに10万件のデータが格納されていることが確認できます。
● マスターサーバ
[admin@kvs002 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 100000
STAT total_items 100000● スレーブサーバ
[admin@kvs003 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 100000
STAT total_items 100000 - プロキシサーバにてflush_allコマンドを投入 まずは、プロキシサーバにflush_allコマンドを投入してみます。
[admin@kvs004 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
flush_all
OK
quit
Connection closed by foreign host. - 各ストレージサーバのレコード数を確認 以下のように、マスターサーバ、スレーブサーバのデータは残っており、共に何も起きていないことがわかります。
● マスターサーバ
[admin@kvs002 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 100000
STAT total_items 100000
[admin@kvs002 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
VALUE test00000000 0 50
01234567890123456789012345678901234567890123456789
END● スレーブサーバ
[admin@kvs003 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 100000
STAT total_items 100000
[admin@kvs003 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
VALUE test00000000 0 50
01234567890123456789012345678901234567890123456789
END - マスターサーバにてflush_allコマンドを投入 マスターサーバのflaredに直接telnet接続してflush_allコマンドを投入します。
[admin@kvs002 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
flush_all
OK
quit
Connection closed by foreign host. - 各ストレージサーバのレコード数を確認 以下の通り、マスターサーバのデータは消去されていますが、スレーブサーバのデータは残っています。
● マスターサーバ
[admin@kvs002 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 0
STAT total_items 100000
[admin@kvs002 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
END
quit
Connection closed by foreign host.● スレーブサーバ
[admin@kvs003 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 100000
STAT total_items 100000
[admin@kvs003 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
VALUE test00000000 0 50
01234567890123456789012345678901234567890123456789
END
quit
Connection closed by foreign host. - スレーブサーバにてflush_allコマンドを投入 続いてスレーブサーバにflush_allコマンドを投入します。
[admin@kvs003 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
flush_all
OK
quit
Connection closed by foreign host. - 各ストレージサーバのレコード数を確認 以下の通り、スレーブサーバのデータも消去されました。
● マスターサーバ
[admin@kvs002 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 0
STAT total_items 100000
[admin@kvs002 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
END
quit
Connection closed by foreign host.● スレーブサーバ
[admin@kvs003 ~]$ echo 'stats' | nc localhost 12121 | grep items
STAT curr_items 0
STAT total_items 100000
[admin@kvs003 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get test00000000
END
quit
Connection closed by foreign host.
対処
藤本さんに、flush_allコマンドも全てのサーバに反映されるように改修をお願いしているところですが、暫定的な対処としてサーバ側でflush_allコマンドを無効にすることにしました。
flaredのソースコード2行をコメントアウトしてコンパイルし直します。
src/flared/op_parser_text_node.cc : 82~83行目
// } else if (strcmp(first, "flush_all") == 0) {
// r = static_cast(_new_ op_flush_all(this->_connection, singleton::instance().get_storage()));
この修正を施したflaredでは、flush_allコマンドを発行するとエラーを返します。
[admin@kvs003 ~]$ telnet localhost 12121
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
flush_all
ERROR
まとめ
今回はFlareにおけるflush_allコマンドの不具合と、暫定的な対処方法についてご紹介しました。 次回は、2010/7/1より申込受付を開始した「KVSアルファテストサービス」のサーバ構成について説明したいと思います。