絶対に真似してはいけない。闇のRedash入門

Lobi事業部データエンジニア(自称)の池田です。
この記事はKAYAC Advent Calendar 2018の4日目です。

Redashとは

Redashは様々な種類のデータソースにアクセスできる、OSSの素晴らしいダッシュボードツールです。
以前、本ブログでは、健康的な使用事例を紹介しました。

techblog.kayac.com techblog.kayac.com

今現在のKayacでも、事業の様々な数値や状態を可視化、お問い合わせからの調査等の業務支援ツールとして広く活用されています 本日は、Redashの非健康的な使用事例を紹介します。

すべての始まり【Pythonデータソース】

Lobi - Chat & Game Communityは今年で開発・運用が8年目になるスマホゲーマーSNSサービスです。
Lobiを開発・運用するLobi事業部では、Redashを活用する上で2つの問題を抱えていました。

  1. ShardingされたDBが参照しづらい問題
  2. Detail JSON Parse辛い問題

1つ目の問題は、Sharding*1されたDBをRedashからアクセスする場合、単純なSQL文では水平分割の台数分だけRedashのクエリが必要になります。 2つ目の問題は、8年という長い期間で生まれた、detail_json という名前の、どうみてもJSONな文字列が入ったTEXTカラムが存在するテーブルがあります。単純なSQL文では特定のキーの情報だけ参照するのはとても大変です。

この問題を解決するソリューションとして、Pythonデータソース
このようなPythonデータソースを使うRedashのクエリが誕生する。

import json 
import re 

# query_params validation
validator = re.compile('[0-9]+(,[0-9]+)*')
ids = '{{{ids}}}'
if not validator.match(ids):
    print 'plz Comma-Separated Values format'
    return 

#shard_map access 
shard_map_query = '''
select user_id, shard
from shard_map
where user_id in (%s)
''' % ids
rows = execute_query('mysql main',shard_map_query)['rows']
ids_by_shard = {}
for row in rows:
    shard = row['shard']
    if not shard in ids_by_shard:
        ids_by_shard[shard] = []
    ids_by_shard[shard].append(row['user_id'])
del rows

#query detail json
data_source_by_shard = {
    'shard01': 'mysql shard1',
    'shard02': 'mysql shard2',
}
for current_shard, current_ids in ids_by_shard.items():
    query = '''
    select id, detail_json
    from user_opt 
    where id in (%s)
    ''' % (','.join(map(lambda x:str(x), current_ids)))
    data_source = data_source_by_shard[current_shard]
    rows = execute_query(data_source,query)['rows']
    for row in rows:
        data1 = ''
        data2 = ''
        if row['detail_json'] != '':
            detail = json.loads(row['detail_json'])
            if 'data1' in detail:
                data1 = detail['data1']
            if 'data2' in detail:
                data2 = detail['data2']
        add_result_row(result, {
            'id': row['id'],
            'data1': data1,
            'data2': data2,
        })

add_result_column(result, 'id', '', 'integer')
add_result_column(result, 'data1', '', 'string')
add_result_column(result, 'data2', '', 'string')

このときは最強のソリューションに見えた。

参考文献

tbpgr.hatenablog.com techblog.lclco.com

気軽にエンジニア以外でも扱える【Googleスプレットシートデータソース】

とある平日。
カスタマーサポート担当から『こちらのリストにのっている方のアクセス情報を一覧できない?』 別のある平日。 営業担当から『このリストにのっているグループの発言量を一覧できない?』 そして、一ヶ月後のある平日 『この間の一覧した情報、定期的に自動更新できない?』

等々、様々な業務を行う関係各所から このリストにのっている○○ シリーズの業務支援を依頼されることがあります。 Kayacでは多くの場合、Google スプレットシート*2を使ってリストが渡されます。

そんな依頼にこちらのソリューション

なんと! エンジニア以外でも扱える素敵なデータソースとして、Googleスプレットシートを使えます。
もちろん!Google Apps Scriptも併用して更に便利になるチャンス!
これは仕事が捗ります

そして、簡易アプリケーション爆誕

ここまでは、健康的な範疇の活用方法ですが、 皆様お気づきだろうか、ここまで環境を整えたRedashは以下のように捉えられるということに。

Redash機能 要素
様々なデータソース Model 特に、柔軟に入出力可能なGoogleスプレットシートは扱いやすい
様々なVisualization View 折れ線グラフや棒グラフ、箱ひげ図、サンキーチャートなど。
Pythonデータソース ViewModel 柔軟なデータ処理を行える。module importすれば可能性は無限大

そう、環境の整ったRedashはMVVMとみなすこともできる!!?
そして、いつの間に、Redash上で動く簡易アプリケーションの爆誕することになります。
もちろん、Git管理されてない
そして、事業の中核になるアプリケーションのリポジトリ外なのでコードレビュー対象外 この簡易アプリケーションは非健康的です。保守の観点で…

現在のLobi事業部のRedash運用

保守が必要になるような割と業務で重要なものは、アプリケーションのリポジトリに

redash/query_3121.py
redash/query_3122.py
︙

のように保存してGit管理するケースが出てきています。

まとめ

Redashの登場により、Kayacではいたるところで業務改善が行われました。
PythonデータソースもGoogleスプレットシートデータソースも適切に利用すれば、とても『イノベーティブ』です。
しかし、過度の利用は保守が大変な簡易アプリケーションの生んでしまいます。
用法・用量を守って素晴らしきRedashライフを送りましょう!

そんな感じで、あらゆる業務改善・事業可視化をするエンジニアもカヤックでは募集しております!

次回予告

明日は、id:wyumikokhさんが『gitbookで幸せ計画』について書いてくれるようです。