期限切れのRedisデータを復元してテストしたい

昔に取得した、redisのrdbファイルを復元してテストしたい

#### redis conf の設定 (抜粋)
dbfilename dump.rdb
dir /usr/local/var/db/redis/


## 普通にリストアすると 
```bash
# バックアップ
mv /usr/local/var/db/redis/dump.rdb /usr/local/var/db/redis/dump.rdb.bk
# 前に取得したrdbファイルをコピーして上書き。
cp -i /path/to/backup/rdb/dump.rdb /usr/local/var/db/redis/
# Redis起動
redis-server /path/to/conf/redis.conf
```
## 結果
期限切れのデータはデータロード時に削除されてしまう。。。。
いろいろ調べたがRedis自体の機能として期限切れデータを復元する方法はなかった。


## 回避策
1. システムの時刻をバックアップ取得した時刻まで戻してリストアする。
2. rdb ファイルを 読み込んで json に変換してまた入れる。

http://grokbase.com/t/gg/redis-db/13b5vsy469/disable-all-expires-for-debugging

## 1.システムの時刻をバックアップ取得した時刻まで戻してリストアする。
システム時刻を変更してから、普通にリストアする。
リストア自体はうまくいくが、時刻の設定変えると他のアプリに影響でそうなので不採用。

## 2. rdb ファイルを 読み込んで json に変換してまた入れる。
以下のツールを使う。
https://github.com/sripathikrishnan/redis-rdb-tools
### Install
```bash
pip install rdb-tools
```
### JSONに変換
rdb-tools にバグ？があり。-f を指定するとfilter条件が無効化されるため
-f を指定せずに、リダイレクトする。
(https://github.com/sripathikrishnan/redis-rdb-tools/blob/master/rdbtools/cli/rdb.py#L66)

```bash
# 全体で取得した結果にDB番号が入ってないのでDBを指定して出力。
rdb --command json --db 1 /path/to/backup/rdb/dump.rdb > /path/to/output/dump.json
```

これは全部出力されてしまう。

```bash
# 全体で取得した結果にDB番号が入ってない(はず。。)、なのでDBを指定して出力。
rdb --command json --db 1 -f /path/to/output/dump.json /path/to/backup/rdb/dump.rdb
```

### Redisにインポート
する機能はrdb-toolsにはないので、自前で書く。

```py:load_rdb_json.py
#!/usr/bin/python
import json
import redis
import argparse

class Callbacks(object):

    @staticmethod
    def zaddCallback(key, values, client):
        for value, score in values.items():
            client.zadd(key, value, score)

    @staticmethod
    def saddCallback(key, values, client):
        print "Key:{0}, Values:{1}".format(key, values)
        client.sadd(key, values)


class RedisRdbJsonLoader(object):

    def __init__(self, host, port, callback, timeout_sec=5):
        self.host = host
        self.port = port
        self.callback = callback
        self.timeout_sec = timeout_sec

    def load(self, rdb_json_file, given_db_num=None):
        rdb_json = json.load(rdb_json_file)
        if given_db_num is None:
            self.__load_to_each_db(rdb_json)
        else:
            self.__load_to_db(given_db_num, rdb_json[given_db_num])

    def __load_to_each_db(self, rdb_json):
        # rdb_json_file includes data in order of db num.
        for db_num, db_values in enumerate(rdb_json):
            self.__load_to_db(db_num, db_values)

    def __load_to_db(self, db_num, db_values):
        print "===================="
        print "Load value for db {0}".format(db_num)

        client = self.__get_redis_client(db_num)
        count = 0
        for key, values in db_values.items():
            self.callback(key, values, client)
            count += 1
            print "{0}:Value loaded for key {1}".format(count, key)


    def __get_redis_client(self, db_num):
        return redis.Redis(self.host, self.port, db_num, socket_timeout=self.timeout_sec)

__TYPE_ACTION_MAPPING = {
    # 'string': setCallback,
    # 'list': lpushCallback,
    'set': Callbacks.saddCallback,
    'zset': Callbacks.zaddCallback,
    # 'hash': hsetCallback
}

def get_parser():
    parser = argparse.ArgumentParser(description='Load Jsonized rdis *.rdb file into redis server.')
    parser.add_argument('-H', '--host', help="Hostname of Redis server.", default='localhost')
    parser.add_argument('-p', '--port', help="Port number of Redis server.", default=6379, type=int)
    parser.add_argument('-n', '--db_num', help="Target DB of Redis server to load data. if this value was given, only first element of json data will be used.", default=None, type=int)
    parser.add_argument('-f', '--data_file', help="Path to data file.", required=True, type=argparse.FileType('r'))
    parser.add_argument('-t', '--data_type', help="Redis type to load.", default='zset', choices=__TYPE_ACTION_MAPPING.keys())
    parser.add_argument('--timeout_sec', help="Redis timeout sec.", default=5, type=int)
    return parser


def execute(args):
    with args.data_file as file:
        callback = __TYPE_ACTION_MAPPING[args.data_type]
        RedisRdbJsonLoader(args.host, args.port, callback, args.timeout_sec).load(file, args.db_num)

if __name__ == '__main__':
    execute(get_parser().parse_args())
```
インポートの実行

特定DBにのみインポートする場合

```bash
python load_rdb_json.py -n 1 -f /path/to/output/dump.json
```

全部インポートする場合

```bash
python load_rdb_json.py -f /path/to/output/dump.json
```

### おわり