EMR 4->5 バージョン間で Hue データを移行する

## やりたいこと
EMR メジャーバージョン 4 から 5 へ移行する際、Hue データ（保存クエリ／履歴／共有設定など）をそのまま移動させて使いたい


## 課題背景

EMR のリリースバージョン emr-4 から emr-5 へアップグレードする場合、Hue はバージョン 3.7 から 3.10 へ変わります

- [Amazon EMR](http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/emr-what-is-emr.html) 
- [emr 5.2.1 release components](http://docs.aws.amazon.com/ja_jp/emr/latest/ReleaseGuide/emr-5.2.1/emr-release-components.html)

Hue 3.10 は、ドキュメント管理方法や クエリエディタが刷新されたバージョン

- [Hue 3.10.0](http://jp.gethue.com/hue-3-10-with-its-new-sql-editor-is-out/)

Hue は Web上で Hiveクエリ等を生成／実行し、クエリや結果をチームで共有したりできるツールで、アドホック集計で便利なツールですが、Hue 3.10 はエディタが刷新されているため、デフォルト設定では **旧バージョンのエディタ（beeswaxなど）で生成したデータが参照できない** 状態になります

　


### 発生する問題

#### 1.データ移行中にエラーがでて移行完了できない
hue `dumpdata/loaddata` を使ったデータ移行途中で、エラーが発生する

**例1**
desktop_documentpermissionテーブルのユニークキー違反

```
django.db.utils.IntegrityError: Problem installing fixture 'hue_data_backup-jq.json': Could not load desktop.DocumentPermission(pk=2): (1062, "Duplicate entry '1-read' for key 'desktop_documentpermission_doc_id_60b132dd84ab9469_uniq'")
```

```
mysql> show create table desktop_documentpermission;
CREATE TABLE `desktop_documentpermission` (
  `perms` varchar(10) COLLATE utf8_bin NOT NULL,
  `doc_id` int(11) NOT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),

  ↓ 新たに追加された制約
  UNIQUE KEY `desktop_documentpermission_doc_id_60b132dd84ab9469_uniq` (`doc_id`,`perms`),

  KEY `desktop_documentpermission_fbbb6049` (`doc_id`),
  CONSTRAINT `doc_id_refs_id_6e61f686` FOREIGN KEY (`doc_id`) REFERENCES `desktop_document` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=69682 DEFAULT CHARSET=utf8 COLLATE=utf8_bin 
```

**例2**
"Duplicate entry 'oozie-bundle' for key 'app_label'"
今回は事前に除外しますが、contenttypesをダンプに含めると出ます

```
django.db.utils.IntegrityError: Problem installing fixture 'hue_data_backup_jq.json': Could not load contenttypes.ContentType(pk=43): (1062, "Duplicate entry 'oozie-bundle' for key 'app_label'")
```

**例3**
外部キー制約違反
今回は事前に除外しますが、auth_permission をダンプに含めると出ます

```
Problem installing fixtures:
The row in table 'auth_permission' with primary key '49' has an invalid foreign key:
auth_permission.content_type_id contains a value '17' that does not have a corresponding value in django_content_type.id.
```

などなど


#### 2.データ移行が成功したにもかかわらず、過去データが参照できない
hue `dumpdata/loaddata` を使ってデータ移行した後、Hue 画面上で 旧エディタで作成したクエリが表示できない
（500 エラーが表示される）

- アクセス先URL（Hiveクエリエディタ）
https://hue-hostname:8888/beeswax/execute/design/{id}

- /var/log/hue/runcpserver.log
500エラー発生時、ログには Document matching query does not exist が出力される

```
[18/Jan/2017 15:15:33 +0900] middleware   INFO     Processing exception: Document matching query does not exist.: Traceback (most recent call last):
  File "/usr/lib/hue/build/env/lib/python2.7/site-packages/Django-1.6.10-py2.7.egg/django/core/handlers/base.py", line 112, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/hue/build/env/lib/python2.7/site-packages/Django-1.6.10-py2.7.egg/django/db/transaction.py", line 371, in inner
    return func(*args, **kwargs)
  File "/usr/lib/hue/apps/beeswax/src/beeswax/views.py", line 418, in execute_query
    authorized_get_design(request, design_id)
  File "/usr/lib/hue/apps/beeswax/src/beeswax/views.py", line 671, in authorized_get_design
    design.doc.get().can_read_or_exception(request.user)
  File "/usr/lib/hue/build/env/lib/python2.7/site-packages/Django-1.6.10-py2.7.egg/django/db/models/manager.py", line 151, in get
    return self.get_queryset().get(*args, **kwargs)
  File "/usr/lib/hue/build/env/lib/python2.7/site-packages/Django-1.6.10-py2.7.egg/django/db/models/query.py", line 310, in get
    self.model._meta.object_name)

DoesNotExist: Document matching query does not exist.
[dd/mmm/yyyy hh:mm:ss +0900] access       INFO     xxx.xxx.xxx.xxx user - "GET /beeswax/execute/design/{id} HTTP/1.1"
```


## 解決方法

### 1. 前処理（データ修正）
移行先の新バージョンのMySQL制約に引っかかるデータを、旧バージョンで修正します
※本手順3.で何もエラーが発生しない状況であれば、手順1.はSKIPして構いません
※本手順3.で発生するエラーにあわせて、必要があれば何度か実施します
※データ操作するので、事前にバックアップ（ `hue datadump` ）を取っておくと安心です

#### Hue database 設定を参照

```
旧バージョン
/etc/hue/conf/hue.ini
  [[database]]
    engine = mysql
    name = huedb
    case_insensitive_collation = utf8_unicode_ci
    test_charset = utf8
    test_collation = utf8_bin
    host = ip-xxx.xxx.compute.internal
    user = hue
    test_name = test_huedb
    password = xxxxxxxxxx
```

#### mysql cli で接続
ホスト名／パスワードは hue.ini にあるとおり入力します

```
$ mysql -u hue -h ip-xxx.xxx.compute.internal -p huedb
```

#### データ修正（例）
ここではクエリデータのパーミッション設定に重複がでるケースを想定し（筆者環境でも同エラーが出ました）
解消するクエリを書きます
※発生する問題に合わせて、解消クエリは適宜検討してください

##### キー重複するレコード群を取得
```
mysql> 
  select doc_id,perms,count(*) as cnt 
  from desktop_documentpermission 
  group by doc_id,perms having cnt > 1 
  order by cnt desc;

+--------+-------+-----+
| doc_id | perms | cnt |
+--------+-------+-----+
|   9999 | write |   2 |
|   9999 | read  |   2 |
....
|  99999 | write |   2 |
|  99999 | read  |   2 |
+--------+-------+-----+
```

##### 対象レコードの削除
重複しているレコードのうち片方を削除することにします
（ここでは重複したもののidが小さいほうを対象に選びます）

```
mysql> 
select a.doc_id,a.perms,min(b.id) 
from ( 
  select doc_id,perms,count(*) as cnt 
  from desktop_documentpermission 
  group by doc_id,perms having cnt > 1 
  order by cnt desc 
) a 
left outer join 
desktop_documentpermission b 
  on a.doc_id = b.doc_id and a.perms = b.perms 
group by a.doc_id,a.perms;
```

結果id を指定してDELETEします

```
mysql>
delete from desktop_documentpermission where id in (,,,);
```




### 2. Hue data dump 実行

1.できれいになった Hueデータ をダンプします
**auth.permission と contenttypes は移行先DBに既に値が入っているため、ダンプ時に除外** します


```
旧バージョン
$ sudo /usr/lib/hue/build/env/bin/hue dumpdata \
  --exclude auth.permission \
  --exclude contenttypes \
  > hue_data_backup.json
```



### 3. Hue data load 実行
旧バージョンで取得したダンプファイルを新バージョンのクラスタへ運び、load します
*jq コマンドはお好みですが、loadエラーが発生したときに問題箇所を探しやすいのでおすすめです*

```
新バージョン
$ cat hue_data_backup.json | jq . > hue_data_backup-jq.json
$ sudo /usr/lib/hue/build/env/bin/hue loaddata hue_data_backup-jq.json
Installed 999999999 object(s) from 999 fixture(s)

```

#### loaddata でエラー発生したときは
基本的にMySQL上の制約に引っかかるエラーが出ます
エラー内容を確認して、MySQL上のテーブル制約（UNIQUE KEY／FOREIGN KEY等）を確認し、制約に違反しないデータに修正します
※loadが成功するまで、手順1.から繰り返します


### 4. Hue DB レコード修正
新バージョンでは、旧エディタで作成されたデータを新エディタで開こうとしてエラー発生させるため、レコードを直接修正します

#### Hue database 設定を参照
手順1と同様ですが念のため

```
新バージョン
/etc/hue/conf/hue.ini
  [[database]]
    engine = mysql
    name = huedb
    case_insensitive_collation = utf8_unicode_ci
    test_charset = utf8
    test_collation = utf8_bin
    host = ip-xxx.xxx.compute.internal
    user = hue
    test_name = test_huedb
    password = xxxxxxxxxx

```

#### mysql cli で接続
ホスト名／パスワードは hue.ini にあるとおり入力します

```
$ mysql -u hue -h ip-xxx.xxx.compute.internal -p huedb
```

#### Update 文実行
desktop_document テーブル内に入っている旧エディタ作成のクエリは、desktopというアプリに関連付けられています
このデータは新バージョンでnotebookというエディタで開く挙動をし、問題の DoesNotExist エラーを発生させます

そこで（今回の例では、レコードはすべてbeeswaxで作成したクエリデータのため）明示的に beeswax savedquery に書き換えてしまいます
※ほかのエディタ（アプリ）で生成したデータの場合、レコードにあわせクエリ調整する必要があるかもしれません

```
mysql> select * from django_content_type where id in (51,58);
+----+--------------+-----------+-------------+
| id | name         | app_label | model       |
+----+--------------+-----------+-------------+
| 51 | document tag | desktop   | documenttag |
| 58 | saved query  | beeswax   | savedquery  |
+----+--------------+-----------+-------------+

mysql> update desktop_document set content_type_id = 58 where content_type_id = 51;
```

### 5. Hue 画面から確認
下記のようなURLにアクセスし、正常に表示されることを確認します

https://hue-hostname:8888/home2
https://hue-hostname:8888/beeswax/execute/design/{id}

beeswax クエリに関しては、この方法で問題なく表示されました

また、上記手順を行ったうえであれば、新エディタ（notebook）画面上のSavedQuery／履歴 タブ内に
beeswax で作成したクエリ群がnotebook 形式で開けるリンクが作成されました
*（Hue-3.10ではSavedQueryのみ、Hue-3.11以降であれば履歴も表示される）*

https://hue-hostname:8888/notebook/editor


移行完了です！　


## 参考資料
- https://community.cloudera.com/t5/Web-UI-Hue-Beeswax/What-is-the-easiest-way-to-back-up-Hue-s-quot-saved-queries-quot/td-p/29931
- [Configuring the Hue Server to Store Data in MySQL](https://www.cloudera.com/documentation/enterprise/5-6-x/topics/cm_mc_hue_service.html#cmig_topic_15_1)  
CDH で Hueデータ移行を行う際の手順を参考にしました
- https://issues.cloudera.org/browse/HUE-4346  
新エディタ（notebook）上で SavedQuery／履歴 を表示させる対応（emr-3.11）


    
## おわりに

以上、同じ問題に直面した方向けの共有でした

問題解消の助けとなればさいわいです、不明瞭な点あればコメント等いただければ対応しますmm