Redash のクエリー結果が自動的にクリーンアップされない場合があるので調査しました

きっかけはこちらの投稿です。フォーラムへの投稿ありがとうございます!

discuss.redash.io

遅くなってしまいましたが、私の理解も改めるきっかけになったので調査結果を残しておきます。

現象

Redash のクエリーが自動的にクリーンアップされる設定をしているにも関わらず、ディスク容量が開放されないとのことでした。

当初の私は、一度にクリーンアップする結果の数を指定する QUERY_RESULTS_CLEANUP_COUNT と、クリーンアップ対象の結果のオフセット(日単位)を指定する QUERY_RESULTS_CLEANUP_MAX_AGE の設定によっては、クリーンアップが遅れたり、自動実行されているクエリーが大量に存在する場合は、クリーンアップが追いつかずに結果が溜まってしまうのかと思っていましたが、どうやら違う原因もあるようでした。

調査内容

調査には安定版の v8.0.0 を使用しました。

クリーンアップに関わるコードを追っていく

クエリー結果のクリーンアップは Celery のタスクで実行されており、以下にタスクが定義されています。

https://github.com/getredash/redash/blob/v8.0.0/redash/tasks/queries.py#L235

クリーンアップ対象のクエリー結果は、以下のコードで取得しています。

    unused_query_results = models.QueryResult.unused(settings.QUERY_RESULTS_CLEANUP_MAX_AGE).limit(settings.QUERY_RESULTS_CLEANUP_COUNT)

ここで呼び出されている models.QueryResult クラスの unused メソッドは以下のように定義されています。

Redash のコードやメタデータの定義に詳しい方は、このコードを見た時点で気になることが見つかるかもしれません。

https://github.com/getredash/redash/blob/v8.0.0/redash/models/__init__.py#L266

unused メソッドの中で実行されている SQL

unused メソッドで実行されている SQL を見てみると、以下のようになっていました。

SELECT query_results.id AS query_results_id 
FROM query_results LEFT OUTER JOIN queries ON query_results.id = queries.latest_query_data_id 
WHERE queries.id IS NULL AND query_results.retrieved_at < %(retrieved_at_1)s 
LIMIT %(param_1)s

参考として、Redash のメタデータqueriesquery_results のテーブル定義を紹介しておきます。

                                         Table "public.queries"
        Column        |           Type           |                      Modifiers                       
----------------------+--------------------------+------------------------------------------------------
 updated_at           | timestamp with time zone | not null
 created_at           | timestamp with time zone | not null
 id                   | integer                  | not null default nextval('queries_id_seq'::regclass)
 version              | integer                  | not null
 org_id               | integer                  | not null
 data_source_id       | integer                  | 
 latest_query_data_id | integer                  | 
 name                 | character varying(255)   | not null
 description          | character varying(4096)  | 
 query                | text                     | not null
 query_hash           | character varying(32)    | not null
 api_key              | character varying(40)    | not null
 user_id              | integer                  | not null
 last_modified_by_id  | integer                  | 
 is_archived          | boolean                  | not null
 is_draft             | boolean                  | not null
 schedule             | text                     | 
 schedule_failures    | integer                  | not null
 options              | text                     | not null
 search_vector        | tsvector                 | 
 tags                 | character varying[]      | 

                                      Table "public.query_results"
     Column     |           Type           |                         Modifiers                          
----------------+--------------------------+------------------------------------------------------------
 id             | integer                  | not null default nextval('query_results_id_seq'::regclass)
 org_id         | integer                  | not null
 data_source_id | integer                  | not null
 query_hash     | character varying(32)    | not null
 query          | text                     | not null
 data           | text                     | not null
 runtime        | double precision         | not null
 retrieved_at   | timestamp with time zone | not null   

SQL とテーブル定義を読み解くと。queries テーブルの last_modified_by_id で参照されていない、かつ、retrieved_at が指定の日時(現在時刻 - 環境変数で指定したオフセット)より古い query_resultsid を抽出しているということがわかります。

ここだけ見ると、クリーンアップ対象となるクエリーを抽出できていそうにみえますが、この SQL ではクリーンアップ対象として取得できないクエリー結果が残ってしまいます。

どんなクエリー結果が残ってしまうのか

前述の SQL でクリーンアップ対象として取得できないクエリー結果は「アーカイブされたクエリーのクエリー結果」となります。

Redash のクエリーはアーカイブという状態をもっており、queries テーブルの is_archivedtrue のものはアーカイブ状態として扱われています。

ここで先程のクエリーを見直してみると、latest_query_data_id の関連やクエリー結果の取得日時は条件として指定されていますが、クエリーがアーカイブされているものは考慮されていないため、アーカイブされたクエリーが最後に取得した結果 はクリーンアップされず延々と残ってしまいます。

この挙動が意図的なものかどうかはわかりませんが、アーカイブされたクエリー結果もクリーンアップするためには、クエリーのアーカイブ処理やクリーンアップ処理周りの修正が必要になると思われます。

残ってしまったクエリー結果をどうするか

アーカイブしたクエリー結果が残ってしまった場合、設定などで簡単に回避できるものではないため、運用に影響がない場合はそのままにするのが良いと考えています。

まとめ

  • アーカイブ済みのクエリーで、最後に実行されたクエリー結果はクリーンアップ対象にされず、DB 上に残ってしまう
  • 2021年2月時点ではアーカイブされたクエリーのクエリー結果を削除するよい方法は無いため、運用上の問題がなければそのままにすることをおすすめ