当ブログをご覧の皆様こんにちは。さくらインターネット研究所の大久保です。
先日、Flareにてflush_allコマンドを使用するとデータの不整合が発生するという事象が発生しました。
今回はその調査内容ついてご説明したいと思います。
不具合の内容
以前の記事ではご紹介していませんでしたが、memcachedプロトコルには保存されている全データを消去するflush_allコマンドというものがあります。Flareにおいてもflush_allコマンドはサポートされていますが、このコマンドを発行するとレプリケーションをとっている(スレーブサーバを設置している)場合に、サーバ間でデータの不整合が発生します。
開発者のグリー藤本さんにお聞きしたところ、flush_allコマンドは発行したサーバでのみ処理され、レプリケーションはされないとのことでした。そのため、あるサーバのデータは消去されるが、他のサーバのデータは存在したままとなり、問い合わせ先のサーバによってデータが存在したり、存在しなかったり、応答が変化することとなります。
この事象について以下の通り再現試験を行いました。
再現試験の内容
- テスト構成
以下の4台のサーバを用いてテストを行いました。プロキシサーバ、ストレージマスターサーバ、ストレージスレーブサーバにそれぞれflush_allコマンドを発行してどのような結果になるかを確認します。サーバ名 役割 IPアドレス kvs001 インデックスサーバ 192.168.12.151 kvs002 ストレージサーバ(マスター) 192.168.12.152 kvs003 ストレージサーバ(スレーブ) 192.168.12.153 kvs004 プロキシサーバ 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アルファテストサービス」のサーバ構成について説明したいと思います。