redashに接続されるデータソースを運用する際に確認しておきたいredashのmetadata

この記事は [Redash Advent Calendar 2017](https://qiita.com/advent-calendar/2017/redash) 20日目の記事です。

18日目の[Redashのmetadataから便利な情報を取ってくる話](https://qiita.com/lab0926/items/89681649decfc378c36a)と似たような内容になっています。

### 概要

Redashのデータソースとなるようなデータベース（Redshiftなど）を運用していると、まれに酷いクエリ（実行に時間のかかるクエリや、エラーになるクエリ）も定期的に打たれるようになってきて辛い思いをするようにもなります。

特にRedshiftを運用していると、厳密にどのクエリがエラーを発生させたのかを後から追跡することが難しかったり、[STL_ERROR](http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/r_STL_ERROR.html)に結構な勢いでエラーログが溜まっていくということがあったりします。

ここでは、そんな辛い思いをするときによく確認するクエリを整理しておきます。
バージョンとしては[schedule_failures](https://github.com/getredash/redash/blob/master/migrations/versions/d1eae8b9893e_.py) が導入された2.0以降の利用を想定しています。

### 利用するテーブル

Redashのデータが格納されるPostgreSQLをデータソースとして登録し、主に以下の2つのテーブルを利用します。

| テーブル名 | 概要 | 
|:---|:---| 
|query_results |Queryの実行結果が格納される| 
|queries |Queryの情報が格納される| 

#### 注意点

* この2つのテーブルをJoinする場合は QUERY_HASH を使う。    
  Queryを変更してしまうと別物となるので注意が必要です。
* query_resultsには、実行に失敗した情報は格納されません。
* queriesの SCHEDULE_FAILURES にはエラーが発生した回数が累計で登録されていますが、実行が成功すると0になります。

Redashで確認できることはそこまで多くは無いので別途データソース側のシステムテーブルなども確認は必要なります。

### スケジュール実行していてエラーになっているクエリを探す

今日発生したエラーの確認をします。
どんなエラーが発生したのかはDBには保存されないので注意が必要です。

```sql

SELECT 
  id,
  name,
  query,
  query_hash,
  schedule,
  schedule_failures
FROM
  queries
WHERE
  schedule_failures != 0
  AND updated_at >= CURRENT_DATE
ORDER BY 
  schedule_failures DESC
;
```
![redash_meta2.png](https://qiita-image-store.s3.amazonaws.com/0/17814/012cc634-4a00-bbd1-6036-169fb7e1ae90.png)


queries.schedule_failures はクエリの実行に成功した際に[0に戻る](https://github.com/getredash/redash/blob/a9fc220ec83b997467bc5f5eae59d244618ea0ea/redash/tasks/queries.py#L466)ので、成功と失敗が混在するようなケース※だとクエリを実行するタイミングによってはこのクエリで確認することはできないかもしれません。

※ 例えばデータの件数が0件だとdivision by zeroが発生するが、常に0件という場合では無いような場合。

### 当日の実行回数の多いクエリーを探す

当日の実行回数の多いクエリを確認します。
以下の例では、10回以上実行しされているクエリのみを抽出しています。
（ただし実行に失敗した場合はカウントされません。）

```sql
WITH q_r AS (
  SELECT
    query_hash AS q_r_hash,
    COUNT(query_hash) AS run_count,
    MAX(retrieved_at) AS recent_exec
  FROM
    query_results 
  WHERE
    retrieved_at >= CURRENT_DATE  -- 当日のデータのみを対象にする
  GROUP BY 
    query_hash
)
SELECT 
  q.id,
  q.query_hash,
  q_r.run_count,
  q_r.recent_exec,
  q.schedule
FROM
  queries as q
  JOIN q_r 
  ON query_hash = q_r_hash
WHERE
  run_count > 10 -- 10回より多く実行されたクエリのみ取得
ORDER BY 
  run_count DESC
;
```

![redash_meta_1.png](https://qiita-image-store.s3.amazonaws.com/0/17814/6b493939-75eb-f727-3be6-c21e7ac16881.png)

このクエリや、下記の実行時間のかかっているクエリを利用し、DATA_SOURCE_ID で絞り込んで QUERY の内容をみて不必要に頻繁に実行され負荷をかけているクエリを洗い出したりします。
（実際は、nameやqueryなど出力する形で利用するかと思います。）

SCHEDULEを見ると、Every day at xx:xxのようなクエリが比較的上位に上がってきたりして、若干？となりますがある程度の傾向は見えるかと思います。

### 実行時間のかかっているクエリを探す

過去1週間で、平均的に時間のかかっているクエリを探すには以下のようにします。
実際には、この情報を元に接続先のデータソースでSlow Queryとなっていないか別途確認するなどが必要になるかと思います。

```sql

WITH q_r AS (
  SELECT
    query_hash AS q_r_hash,
    COUNT(query_hash) AS run_count,
    AVG(runtime) AS avg_spend -- 単位はsec
  FROM
    query_results 
  WHERE
    retrieved_at >= CURRENT_DATE - integer '7'  -- 過去1週間に実行されたクエリを対象にする
  GROUP BY
    query_hash
)
SELECT 
  q.id,
  q.query_hash,
  q_r.run_count,
  q_r.avg_spend,
  q.schedule
FROM
  queries as q
  JOIN q_r 
  ON query_hash = q_r_hash
WHERE
  run_count > 7
ORDER BY
  avg_spend DESC
;

```