mongify を使って MySQL から MongoDB にデータ移行

mongify を使って、MySQL から MongoDB にデータを移行したので、その手順をまとめておく。

**注意!**
[mongifyは多段（複層）embed（`country < state < city` のような関係）には対応していない！](https://github.com/anlek/mongify/issues/64)
※mongify1.3.0にて非対応を確認済み
※あくまでもmongifyが対応していないというだけで、mongodb や mongoid は対応している。

## mongify のインストール

mongifyが正常に動作する環境を作るのに苦労した。バージョンの制約が激しいので、bundlerで使用するgemを指定するのをオススメ。
※OSはWindows8.1。

動作したバージョンの組み合わせ

| gem | Ruby2.0, 2.1, 2.3で確認 |
|:-:|:-:|
| mongify  | 1.3.0<BR>※1.2.4ではNG  |
| activesupport  | 4.2.7.1 |
| activerecord  | 4.2.7.1 |
| activerecord-mysql2-adapter | 0.0.3 |
| mysql2 | 0.4.4 |
| bson  | 1.10.2 もしくは 1.12.5 |
| bson_ext  | 1.10.2 もしくは 1.12.5 |
| mongo  | 1.10.2 もしくは 1.12.5 |

参考までにGemfileを。環境によってはもっと色々記述する必要がある。

```rb:Gemfile
# frozen_string_literal: true
source "https://rubygems.org"

gem "activesupport", '~>4.2'
gem "activerecord", '~>4.2'
gem 'mongify', '1.3.0'
gem 'mongo', '~>1.10'
gem 'mysql2'
gem 'activerecord-mysql2-adapter'
```

また、activerecord-mysql2-adapter のinstallで、以下の指示が表示された。

```
> gem install activerecord-mysql2-adapter
Fetching: mysql2-0.4.4-x86-mingw32.gem (100%)

======================================================================================================

  You've installed the binary version of mysql2.
  It was built using MySQL Connector/C version 6.1.6.
  It's recommended to use the exact same version to avoid potential issues.

  At the time of building this gem, the necessary DLL files were retrieved from:
  http://cdn.mysql.com/Downloads/Connector-C/mysql-connector-c-6.1.6-win32.zip

  This gem *includes* vendor/libmysql.dll with redistribution notice in vendor/README.

======================================================================================================
```

分かったような分からないような記述でちょっと困ったが、記載されているzipファイルをダウンロードし、[こちら]
(http://www.rubylife.jp/rails/model/index3.html)を参考にして、ruby.exeが格納されているディレクトリに libmysql.dll をコピーした。

## 接続設定ファイルの作成

```rb:database.config（ファイル名は何でもOK）
sql_connection do
  adapter   "mysql2"     ← ★"mysql"も試したが、最終的にはこれでいけた.

  host      "localhost"
  username  "root"
  password  "xxx"
  database  "xxx"
end

mongodb_connection do
  host      "localhost"
  database  "xxx"

  # mLab MongoDB 用にはこれらの設定も必要
  # host      "dxxxxxx.mlab.com"
  # database  "xxx"
  # port      "37814"
  # username  "ユーザー名"
  # password  "パスワード"
end
```

## 接続確認 `mongify check` の実行
bundler を使う場合は、コマンドの前に bundle exec を付ける。（以降同じ）

```
> mongify check f:\db\tmp\database.config
DL is deprecated, please use Fiddle
SQL connection works
NoSQL connection works
```

※参考: NGログ

```
> mongify check f:\db\tmp\database.config
C:/RailsInstaller/Ruby2.2.0/lib/ruby/site_ruby/2.2.0/rubygems/dependency.rb:310:in `to_specs': Please install the mysql2
 adapter: `gem install activerecord-mysql2-adapter` (Could not find 'mysql2' (~> 0.3.10) - did find: [mysql2-0.4.2-x86-m
ingw32] (LoadError)
Checked in 'GEM_PATH=xxx)
```

## 既存データベースからテーブル情報を取得 `mongify translation` の実行

```
> mongify translation f:\db\tmp\database.config > f:\db\tmp\translation.rb
```

## transration.rb の中身をしっかり編集
 - referrenceにするのかembedするのか ※多段（複層）のembedにはmongifyが対応していないので注意。

 - 不要カラムを移行対象から外す ※idカラム(恐らく:keyが付いているカラム）は自動的に削除される
 - 不要テーブル（schema_migrationsなど）を移行対象から外す

## データ移行 `mongify process` の実行

MySQLのdumpファイルサイズがzip圧縮で1MBちょっとしかなくても、移行には20分近くかかった。（embed と referrence処理に時間がかかる。）

```
> mongify process f:\db\tmp\database.config f:\db\tmp\translation.rb
DL is deprecated, please use Fiddle
Copying xxxs (1/1):                      (163/163) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00
:
Updating References xxxs:                (163/163) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00
:
Embedding yyyys (1/3):               (10000/10000) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:01:50
:
Removing pre_mongified_id xxxs:              (1/1) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00
```

参考: mongify 1.2.4 で発生したエラー。ActiveRecordのバージョンが合っていない、もしくは mongify が古いのが原因だと思われる。

```
> mongify process f:\db\tmp\database.config f:\db\tmp\translation.rb
DL is deprecated, please use Fiddle
C:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/mongify-1.2.4/lib/mongify/database/column.rb:246:in `type_cast': un
defined method `string_to_time' for ActiveRecord::ConnectionAdapters::Column:Class (NoMethodError)
```

参考: 大量データを処理していると、最後の最後にエラーが発生することも(-_-)

どうやら Cursor がタイムアウト（MongoDBのデフォルトでは10分らしい）に引っかかってしまったようだ。

```
Updating References xxxs:           (21815/21815) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:07:16
C:/Ruby/Ruby23/lib/ruby/gems/2.3.0/gems/mongo-1.10.2/lib/mongo/networking.rb:221:in `check_response_flags': Query respon
se returned CURSOR_NOT_FOUND. Either an invalid cursor was specified, or the cursor may have timed out on the server. (M
ongo::OperationFailure)
        from C:/Ruby/Ruby23/lib/ruby/gems/2.3.0/gems/mongo-1.10.2/lib/mongo/networking.rb:214:in `receive_response_heade
r'
```

timeout回避のために、サービス起動引数に以下を追加してみたが効果なし。

```
--setParameter cursorTimeoutMillis=1200000
```

仕方ないので mongify 1.3.0 のソースコードを自前で修正した。
https://github.com/leon-joel/mongify
※目的地には限りなく遠回りしているような気もするが…

mongify.gemspecを適当に修正して、`gem build` する。

そして、gemをインストール。

```bash
gem install --local ＜gemファイルパス＞ 
```

Gemfileを適当に修正して、bundle install/update などする。

で、再度実行すると、タイムアウトにかかることなく処理が完了した。
但し、処理時間が倍くらい（30分弱）かかるようになった。
※ちなみに、同じデータをクラウドのMongoDB（Herokuアドオンの『mLab MongoDB』）に移行したときは、トータル8時間ほどかかった。
クラウドへの移行は、一旦ローカルのMongoDBに移行して、そこから `mongodump/mongorestore`でクラウドに移行するのをお薦め。

```bash
> bundle exec mongify process mlab.config translation_fix.rb
DL is deprecated, please use Fiddle
DL is deprecated, please use Fiddle
Copying xxxxxxx (1/1):                    (163/163) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:01
Copying xxxxxxx (1/1):                  (1088/1088) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:03
Copying xxxxxxx (1/3):                (10000/10000) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:22
Copying xxxxxxx (2/3):                (10000/10000) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:20
Copying xxxxxxx (3/3):                  (1815/1815) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:03
Copying xxxxxxx (1/3):                (10000/10000) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:20
Copying xxxxxxx (2/3):                (10000/10000) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:20
Copying xxxxxxx (3/3):                  (6010/6010) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:12
Updating References xxxxx:                (163/163) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00
Updating References xxxxx:              (1088/1088) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:07
Updating References xxxxx:            (21815/21815) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 03:02:28
Updating References xxxxx:            (26010/26010) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 04:31:40
Removing pre_mongified_id xxxxxx:             (1/1) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00
Removing pre_mongified_id xxxxxx:             (1/1) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00
Removing pre_mongified_id xxxxxx:             (1/1) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:01
Removing pre_mongified_id xxxxxx:             (1/1) 100% |ooooooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:01
```

※ちなみにちなみに、DBサイズは PostgreSQLの時は約8MBだったが、MongoDBに移行したら約16MBになった。

## 確認
意図した通りにtranslationされているかどうかを必ず確認すること。

 - referrence/embedが正しく行われているか（先頭のデータだけではなく、末尾のデータまで確認が必要）
 - オリジナルのidが必要ならちゃんとそれを残しているか
 - 不要なカラムを移行してきていないか




----
## 参考
[mongifyの公式 getting started ページ ※コンパクトにまとまっていて分かりやすい]
(http://mongify.com/getting_started.html)