ホーム » 技術 » Key Value Store » Flareを使う(flush_allコマンドの不具合)

SAKURA Internet Inc.

アーカイブ

Flareを使う(flush_allコマンドの不具合)

当ブログをご覧の皆様こんにちは。さくらインターネット研究所の大久保です。
先日、Flareにてflush_allコマンドを使用するとデータの不整合が発生するという事象が発生しました。
今回はその調査内容ついてご説明したいと思います。

不具合の内容

以前の記事ではご紹介していませんでしたが、memcachedプロトコルには保存されている全データを消去するflush_allコマンドというものがあります。Flareにおいてもflush_allコマンドはサポートされていますが、このコマンドを発行するとレプリケーションをとっている(スレーブサーバを設置している)場合に、サーバ間でデータの不整合が発生します。

開発者のグリー藤本さんにお聞きしたところ、flush_allコマンドは発行したサーバでのみ処理され、レプリケーションはされないとのことでした。そのため、あるサーバのデータは消去されるが、他のサーバのデータは存在したままとなり、問い合わせ先のサーバによってデータが存在したり、存在しなかったり、応答が変化することとなります。

この事象について以下の通り再現試験を行いました。

再現試験の内容

  1. テスト構成
    以下の4台のサーバを用いてテストを行いました。プロキシサーバ、ストレージマスターサーバ、ストレージスレーブサーバにそれぞれflush_allコマンドを発行してどのような結果になるかを確認します。

    サーバ名 役割 IPアドレス
    kvs001 インデックスサーバ 192.168.12.151
    kvs002 ストレージサーバ(マスター) 192.168.12.152
    kvs003 ストレージサーバ(スレーブ) 192.168.12.153
    kvs004 プロキシサーバ 192.168.12.154
  2. 各サーバにてデーモンを起動
    設定は以前と同じものを使用するため省略します。
  3. インデックスサーバにて役割設定
    インデックスサーバにて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.
  4. プロキシサーバからテストデータ投入
    以下のように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
  5. 各ストレージサーバのレコード数を確認
    以下のように、マスターサーバ、スレーブサーバに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
  6. プロキシサーバにて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.
  7. 各ストレージサーバのレコード数を確認
    以下のように、マスターサーバ、スレーブサーバのデータは残っており、共に何も起きていないことがわかります。

    ● マスターサーバ

    [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
  8. マスターサーバにて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.
  9. 各ストレージサーバのレコード数を確認
    以下の通り、マスターサーバのデータは消去されていますが、スレーブサーバのデータは残っています。

    ● マスターサーバ

    [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.
  10. スレーブサーバにて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.
  11. 各ストレージサーバのレコード数を確認
    以下の通り、スレーブサーバのデータも消去されました。

    ● マスターサーバ

    [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アルファテストサービス」のサーバ構成について説明したいと思います。