Droongaチュートリアル: データベースのバックアップと復元

チュートリアルのゴール

データのバックアップと復元を手動で行う際の手順を学ぶこと。

前提条件

このチュートリアルでは、1つ前のチュートリアルで準備した2つの既存のDroongaノード:node0 (192.168.100.50) 、 node1 (192.168.100.51) と、作業環境として使うもう1台のコンピュータ node2 (192.168.100.52) があると仮定します。 あなたの手元にあるDroongaノードがこれとは異なる名前である場合には、以下の説明の中のnode0node1node2は実際の物に読み替えて下さい。

Droongaクラスタのデータをバックアップする

drndump のインストール

最初に、作業マシンのnode2にRubygems経由で drndump と名付けられたコマンドラインツールをインストールします:

# gem install drndump

その後、drndumpコマンドが正しくインストールできたかどうかを確認します:

$ drndump --version
drndump 1.0.1

Droongaクラスタ内のデータをダンプする

drndump コマンドはすべてのスキ−マ定義とデータをJSONs形式で取り出します。既存のDroongaクラスタのすべての内容をダンプ出力してみましょう。

例えば、クラスタが node0 (192.168.100.50) と node1 (192.168.100.51) の2つのノードから構成されていて、別のホスト node2 (192.168.100.52) にログインしている場合、コマンドラインは以下の要領です。

# drndump --host=node0 \
           --receiver-host=node2
{
  "type": "table_create",
  "dataset": "Default",
  "body": {
    "name": "Location",
    "flags": "TABLE_PAT_KEY",
    "key_type": "WGS84GeoPoint"
  }
}
...
{
  "dataset": "Default",
  "body": {
    "table": "Store",
    "key": "store9",
    "values": {
      "location": "146702531x-266363233",
      "name": "Macy's 6th Floor - Herald Square - New York NY  (W)"
    }
  },
  "type": "add"
}
{
  "type": "column_create",
  "dataset": "Default",
  "body": {
    "table": "Location",
    "name": "store",
    "type": "Store",
    "flags": "COLUMN_INDEX",
    "source": "location"
  }
}
{
  "type": "column_create",
  "dataset": "Default",
  "body": {
    "table": "Term",
    "name": "store_name",
    "type": "Store",
    "flags": "COLUMN_INDEX|WITH_POSITION",
    "source": "name"
  }
}

以下の点に注意して下さい:

実行結果は標準出力に出力されます。 結果をJSONs形式のファイルに保存する場合は、リダイレクトを使って以下のようにして下さい:

$ drndump --host=node0 \
          --receiver-host=node2 \
    > dump.jsons

Droongaクラスタのデータを復元する

droonga-clientのインストール

drndumpコマンドの実行結果は、Droonga用のメッセージの一覧です。

Droongaクラスタにそれらのメッセージを送信するには、droonga-sendコマンドを使います。 このコマンドを含んでいるGemパッケージ droonga-client を、作業マシンであるnode2にインストールして下さい:

# gem install droonga-client

droonga-sendコマンドが正しくインストールされた事を確認しましょう:

$ droonga-send --version
droonga-send 0.2.1

空のDroongaクラスタを用意する

2つのノード node0 (192.168.100.50) と node1 (192.168.100.51) からなる空のクラスタがあり、今 node2 (192.168.100.52) にログインして操作を行っていて、ダンプファイルが dump.jsons という名前で手元にあると仮定します。

もし順番にこのチュートリアルを読み進めているのであれば、クラスタとダンプファイルが既に手元にあるはずです。以下の操作でクラスタを空にしましょう:

$ endpoint="http://node0:10041"
$ curl "$endpoint/d/table_remove?name=Location" | jq "."
[
  [
    0,
    1406610703.2229023,
    0.0010793209075927734
  ],
  true
]
$ curl "$endpoint/d/table_remove?name=Store" | jq "."
[
  [
    0,
    1406610708.2757723,
    0.006396293640136719
  ],
  true
]
$ curl "$endpoint/d/table_remove?name=Term" | jq "."
[
  [
    0,
    1406610712.379644,
    6.723403930664062e-05
  ],
  true
]

これでクラスタは空になりました。 確かめてみましょう。 以下のように、selecttable_listコマンドは空の結果を返します:

$ curl "$endpoint/d/table_list" | jq "."
[
  [
    0,
    1406610804.1535122,
    0.0002875328063964844
  ],
  [
    [
      [
        "id",
        "UInt32"
      ],
      [
        "name",
        "ShortText"
      ],
      [
        "path",
        "ShortText"
      ],
      [
        "flags",
        "ShortText"
      ],
      [
        "domain",
        "ShortText"
      ],
      [
        "range",
        "ShortText"
      ],
      [
        "default_tokenizer",
        "ShortText"
      ],
      [
        "normalizer",
        "ShortText"
      ]
    ]
  ]
]
$ curl -X DELETE "$endpoint/cache" | jq "."
true
$ curl "$endpoint/d/select?table=Store&output_columns=name&limit=10" | jq "."
[
  [
    0,
    1401363465.610241,
    0
  ],
  [
    [
      [
        null
      ],
      []
    ]
  ]
]

selectコマンドにクエストを送る前に、まずキャッシュを削除しておくことに注意して下さい。 これを忘れると、古い情報に基づいて、キャッシュされた結果が意図せず返されてしまいます。

既定の設定では、レスポンスキャッシュは直近の100リクエストに対して保存され、保持期間は1分間です。 上記のように、/cacheのパスの位置にHTTPのDELETEのリクエストを送信すると、手動でレスポンスキャッシュを削除できます。

ダンプ結果から空のDroongaクラスタへデータを復元する

drndumpコマンドの実行結果はダンプ出力元と同じ内容のデータセットを作るために必要な情報をすべて含んでいます。そのため、クラスタが壊れた場合でも、ダンプファイルからクラスタを再構築する事ができます。 やり方は単純で、単にダンプファイルをdroonga-sendコマンドを使って空のクラスタに流し込むだけです。

ダンプファイルからクラスタの内容を復元するには、以下のようなコマンドを実行します:

$ droonga-send --server=node0  \
                    dump.jsons

注意:

これで、データが完全に復元されました。確かめてみましょう:

$ curl -X DELETE "$endpoint/cache" | jq "."
true
$ curl "$endpoint/d/select?table=Store&output_columns=name&limit=10" | jq "."
[
  [
    0,
    1401363556.0294158,
    7.62939453125e-05
  ],
  [
    [
      [
        40
      ],
      [
        [
          "name",
          "ShortText"
        ]
      ],
      [
        "1st Avenue & 75th St. - New York NY  (W)"
      ],
      [
        "76th & Second - New York NY  (W)"
      ],
      [
        "Herald Square- Macy's - New York NY"
      ],
      [
        "Macy's 5th Floor - Herald Square - New York NY  (W)"
      ],
      [
        "80th & York - New York NY  (W)"
      ],
      [
        "Columbus @ 67th - New York NY  (W)"
      ],
      [
        "45th & Broadway - New York NY  (W)"
      ],
      [
        "Marriott Marquis - Lobby - New York NY"
      ],
      [
        "Second @ 81st - New York NY  (W)"
      ],
      [
        "52nd & Seventh - New York NY  (W)"
      ]
    ]
  ]
]

既存のクラスタを別の空のクラスタに直接複製する

複数のDroongaクラスタが存在する場合、片方のクラスタの内容をもう片方のクラスタに複製することができます。 droonga-engine パッケージはdroonga-engine-absorb-dataというユーティリティコマンドを含んでおり、これを使うと、既存のクラスタから別のクラスタへ直接データをコピーする事ができます。ローカルにダンプファイルを保存する必要がない場合には、この方法がおすすめです。

複数のDroongaクラスタを用意する

ノード node0 (192.168.100.50) を含む複製元クラスタと、ノード node1' (192.168.100.51`) を含む複製先クラスタの2つのクラスタがあると仮定します。

もし順番にこのチュートリアルを読み進めているのであれば、2つのノードを含むクラスタが手元にあるはずです。droonga-engine-catalog-modifyを使って2つのクラスタを作り、1つを空にしましょう。手順は以下の通りです:

(on node0)
# droonga-engine-catalog-modify --replica-hosts=node0
(on node1)
# droonga-engine-catalog-modify --replica-hosts=node1

これらのコマンドによって、2ノードからなる1つのクラスタが、それぞれ1ノードからなる2つのクラスタへ分割されます。 カタログ定義ファイルの変更はdroonga-engineサービスによって自動的に検出され、プロセスが自動的に再起動されます。

この処理には時間がかかるため、完了まで1分程度待つ必要があります。 もしそのノード上で動作中のdroonga-engine-serviceのプロセスが2つ以上あるのであれば、そのノードはまだ再起動処理の途中です。 (新しいサービスのプロセスが動作し始めたら、古いプロセスが自動的に終了します。)

(on node0, node1)
$ ps aux | grep droonga-engine-service | grep -v grep | wc -l
2

しばらく待つと、各ノード上でサービスのプロセスが1つだけ動作している状態になります:

(on node0, node1)
$ ps aux | grep droonga-engine-service | grep -v grep | wc -l
1

これで、2つの別々のクラスタができました:

$ curl "http://node0:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "status": "active"
    }
  },
  "reporter": "..."
}
$ curl "http://node1:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node1:10031/droonga": {
      "status": "active"
    }
  },
  "reporter": "..."
}

では、片方のクラスタを空にしましょう:

(on node1)
$ endpoint="http://node1:10041"
$ curl "$endpoint/d/table_remove?name=Location"
$ curl "$endpoint/d/table_remove?name=Store"
$ curl "$endpoint/d/table_remove?name=Term"
$ curl -X DELETE "http://node1:10041/cache" | jq "."
true
$ curl "http://node1:10041/d/select?table=Store&output_columns=name&limit=10" | jq "."
[
  [
    0,
    1401363465.610241,
    0
  ],
  [
    [
      [
        null
      ],
      []
    ]
  ]
]
$ curl -X DELETE "http://node0:10041/cache" | jq "."
true
$ curl "http://node0:10041/d/select?table=Store&output_columns=name&limit=10" | jq "."
[
  [
    0,
    1401363556.0294158,
    7.62939453125e-05
  ],
  [
    [
      [
        40
      ],
      [
        [
          "name",
          "ShortText"
        ]
      ],
      [
        "1st Avenue & 75th St. - New York NY  (W)"
      ],
      [
        "76th & Second - New York NY  (W)"
      ],
      [
        "Herald Square- Macy's - New York NY"
      ],
      [
        "Macy's 5th Floor - Herald Square - New York NY  (W)"
      ],
      [
        "80th & York - New York NY  (W)"
      ],
      [
        "Columbus @ 67th - New York NY  (W)"
      ],
      [
        "45th & Broadway - New York NY  (W)"
      ],
      [
        "Marriott Marquis - Lobby - New York NY"
      ],
      [
        "Second @ 81st - New York NY  (W)"
      ],
      [
        "52nd & Seventh - New York NY  (W)"
      ]
    ]
  ]
]

droonga-http-serverは同じコンピュータ上のdroonga-engineに関連付けられていることに注意してください。 上記の手順でクラスタを2つに分割した後は、node0droonga-http-servernode0droonga-engineとだけ通信し、node1droonga-http-servernode1droonga-engineとだけ通信します。 詳しくは次のチュートリアルも参照して下さい。

2つのDroongaクラスタの間でデータを複製する

2つのクラスタの間でデータをコピーするには、いずれかのノード上で以下のようにdroonga-engine-absorb-dataコマンドを実行します:

(on node1)
$ droonga-engine-absorb-data --host=node1 \
                             --source-host=node0 \
                             --receiver-host=node1
Start to absorb data from Default at node0:10031/droonga
                       to Default at node1:10031/droonga
                      via node1 (this host)

Absorbing...
Getting the timestamp of the last processed message in the source node...
The timestamp of the last processed message in the source node: 2015-04-29T10:07:08.230158Z
Setting the destination node to ignore messages older than the timestamp...
100% done (maybe 00:00:00 remaining)
Done.

このコマンドは、以下のようにして別のノード上で実行することもできます:

(on node2)
$ droonga-engine-absorb-data --host=node1 \
                             --source-host=node0 \
                             --receiver-host=node2
Start to absorb data from Default at node0:10031/droonga
                       to Default at node1:10031/droonga
                      via node2 (this host)
...

この時、コマンドを実行するノードのホスト名かIPアドレスを--receiver-hostオプションで指定する必要があることに注意してください。

以上の操作で、2つのクラスタの内容が完全に同期されました。確かめてみましょう:

$ curl -X DELETE "http://node1:10041/cache" | jq "."
true
$ curl "http://node1:10041/d/select?table=Store&output_columns=name&limit=10" | jq "."
[
  [
    0,
    1401363556.0294158,
    7.62939453125e-05
  ],
  [
    [
      [
        40
      ],
      [
        [
          "name",
          "ShortText"
        ]
      ],
      [
        "1st Avenue & 75th St. - New York NY  (W)"
      ],
      [
        "76th & Second - New York NY  (W)"
      ],
      [
        "Herald Square- Macy's - New York NY"
      ],
      [
        "Macy's 5th Floor - Herald Square - New York NY  (W)"
      ],
      [
        "80th & York - New York NY  (W)"
      ],
      [
        "Columbus @ 67th - New York NY  (W)"
      ],
      [
        "45th & Broadway - New York NY  (W)"
      ],
      [
        "Marriott Marquis - Lobby - New York NY"
      ],
      [
        "Second @ 81st - New York NY  (W)"
      ],
      [
        "52nd & Seventh - New York NY  (W)"
      ]
    ]
  ]
]

2つのDroongaクラスタを結合する

これらの2つのクラスタを結合するために、以下のコマンド列を実行しましょう:

(on node0)
# droonga-engine-catalog-modify --add-replica-hosts=node1
(on node1)
# droonga-engine-catalog-modify --add-replica-hosts=node0

これで、1つだけクラスタがある状態になりました。最初の状態に戻ったという事になります。 (もちろん、サービスが完全に再起動されるまでしばらく待つ必要があります。)

$ curl "http://node0:10041/droonga/system/status" | jq "."
{
  "nodes": {
    "node0:10031/droonga": {
      "status": "active"
    },
    "node1:10031/droonga": {
      "status": "active"
    }
  },
  "reporter": "..."
}

まとめ

このチュートリアルでは、Droongaクラスタのバックアップとデータの復元の方法を実践しました。 また、既存のDroongaクラスタの内容を別の空のクラスタへ複製する方法も実践しました。

続いて、既存のDroongaクラスタに新しいreplicaを追加する手順を学びましょう。