Redashの長期運用を見据えてクエリの名前付けについて考える

この記事はRedash Advent Calendar 2017 24日目の記事です。

qiita.com

Redashのクエリも名前付け重要

Redashはフォーク機能によって、あるクエリをもとにした派生クエリを作成することが簡単になっており、新規クエリもUIから簡単に作成できるため、お手本のクエリを参考にしつつ、利用者が欲しいデータをエンジニアを介さずに抽出することができるようになります。

しかし、クエリの作成・フォークがカジュアルにできることによって、「クエリの名前付け」問題が少しずつ影を落としはじめるのも事実です。

クエリ名前付けに関わる問題としては、以下のような問題が考えられます。

  • 何をするためのクエリなのかわからない
  • 同じような名前のクエリがいくつもある
  • フォークを繰り返した結果、"Copy of (#2) Copy of (#1).." のようなクエリが乱立する

過去にはエウレカさんのエンジニアブログでも、「命名規則」についても言及されており、Redash運用において、名前付け問題が避けて通れないことを表しているようにも感じます。

developers.eure.jp

クエリの命名規則を考える

どのような情報をクエリ名に埋め込むべきかは、Redashの導入目的や運用によって異なるものだと思いますが、この記事では例として、以下のようにシンプルな命名規則を定義することにします。

[プロジェクト]_[データソース]_[説明]

情報量の多いクエリ名ではありませんが、作成者や作成日時はクエリ一覧画面で確認することができる一方、どのプロジェクトや案件で使われているのか、どのデータソースを使用しているのかは一覧で表示されないものになるため、このような命名規則としてみました。

kakakakakku/redash-hands-onの資料を例に取ってみると 国の一覧 というクエリ名は ハンズオン_MySQL_国の一覧 のように書き換えられます。

命名規則を運用することの難しさ

シンプルな命名規則を定義できたのであれば、あとは運用するだけです。利用者に周知し、必要であればマニュアル化し、命名規則を徹底してもらいましょう。

さて、これで本当にすべてのクエリが命名規則に従って運用されるでしょうか?

悪気がなくてもクエリの命名規則を間違えてしまったり、フォークしたときの名前をそのまま使ってしまう、緊急対応で命名規則に注意が向かないなど、人間が作業をする以上、100%実行することは難しいと考えています。

クエリ名を定期的にチェックする

人間は必ず間違える、という前提に立ちつつ、命名規則を健全に運用するにはどうするのが良いでしょうか。

私のアイディアとしては、クエリ名を定期的かつ自動的にチェックし、命名規則違反や重複などを検出するのがいいと考えています。

Advent Calendarでもいくつか紹介のあった、Redashのメタデータを使用する方法も考えられますが、この記事では、 redashmanPythonスクリプトを使って、クエリ名が命名規則に従っているかをチェックしてみます。

サンプルスクリプト

redashman がインストールされている前提で、以下のPythonスクリプトを使用します。

#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function

import json
import os
import subprocess
import re

REDASH_API_KEY = os.environ.get('REDASH_API_KEY')
REDASH_URL = os.environ.get('REDASH_URL')
PAGE_SIZE = 100
NAMING_CONVENTION_PATTERN = '\A[^_]+_[^_]+_[^_]+\Z'

names = set()
page = 1

while True:
    cmd = 'redashman query list %d %d --api-key=%s --url=%s --json' % (
        PAGE_SIZE, page, REDASH_API_KEY, REDASH_URL)
    stdout, stderr = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE, shell=True).communicate()
    if stderr:
        break

    queries = json.loads(stdout)
    for query in queries['results']:
        name = query['name']
        warning = ''
        if re.match(NAMING_CONVENTION_PATTERN, name) is None:
            warning = u'%s (id: %d) must be follow the naming convention' % (name, query['id'])
        elif 'Copy of' in name.encode('utf-8'):
            warning = u'%s (id: %d) contains "Copy of" in its name' % (name, query['id'])
        elif name in names:
            warning = u'%s (id: %d) is duplicated' % (name, query['id'])

        if warning:
            print(warning)

        names.add(name)

    page += 1

このスクリプトでは、 redashman でクエリの一覧を取得し、

  • [プロジェクト]_[データソース]_[説明] の形式になっていない
  • "Copy of" を含んでいる(Forkしたままのクエリ名になっている)
  • 他のクエリ名と重複している

上記のいずれかの条件に一致する場合は警告メッセージを表示するようになっています。

動作確認

以下のようにクエリが登録してあることを前提として、動作確認します。

f:id:ariarijp:20171223212037p:plain

命名規約違反やクエリ名重複など、運用で困りそうなクエリ名が見受けられますね。

$ export REDASH_URL="http://localhost"
$ export REDASH_API_KEY="adminユーザーのAPIキー"
$ python query_naming_convention.py
ハンズオン_MySQL_都市の一覧 (id: 6) is duplicated
New Query (id: 5) must be follow the naming convention
緊急調査用 (id: 4) must be follow the naming convention
Copy of (#2) Copy of (#1) ハンズオン_MySQL_国の一覧 (id: 3) contains "Copy of" in its name
Copy of (#1) ハンズオン_MySQL_国の一覧 (id: 2) contains "Copy of" in its name

実行してみると、先程定義したルールに従ってクエリ名がチェックされていることがわかります。

このサンプルコードではシンプルにメッセージを表示するだけになっていますが、結果をSlackで送るようにするなどして、運用者が命名規則違反に気づくことができれば、対症療法的な面はありつつも、ルールが破綻しないように運用することができると思います。

Pythonで書いていることを活かし、クエリ名の類似度を取って、紛らわしい名前付けを発見するなどの応用も効きそうです。

まとめ

Redashを長期間運用すると、何に使っているのかわからないようなクエリが混乱のもとになることがあります。

クエリの利用状況をメタデータなどでウォッチすることも有用ではありますが、利用者が見つけやすく、クエリの作成目的がひと目で分かるようなクエリの名前付けを意識し、それを定期的にチェックすることで、ルールの破綻を防ぐことができると考えます。

実のところ、現状は私が運用しているRedashでも、クエリ命名に一貫性がないものが多く、似たような名前のクエリによって調査や対応に手間取るようなことも起き始めています。

せっかくAdvent Calendarの記事にしたので、来年はこの記事で紹介したスクリプトを使って、少しずつ運用を改善していきたいと思います。