Redash のクエリー結果が自動的にクリーンアップされない場合があるので調査しました
きっかけはこちらの投稿です。フォーラムへの投稿ありがとうございます!
遅くなってしまいましたが、私の理解も改めるきっかけになったので調査結果を残しておきます。
現象
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 のメタデータの queries と query_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_results の id を抽出しているということがわかります。
ここだけ見ると、クリーンアップ対象となるクエリーを抽出できていそうにみえますが、この SQL ではクリーンアップ対象として取得できないクエリー結果が残ってしまいます。
どんなクエリー結果が残ってしまうのか
前述の SQL でクリーンアップ対象として取得できないクエリー結果は「アーカイブされたクエリーのクエリー結果」となります。
Redash のクエリーはアーカイブという状態をもっており、queries テーブルの is_archived が true のものはアーカイブ状態として扱われています。
ここで先程のクエリーを見直してみると、latest_query_data_id の関連やクエリー結果の取得日時は条件として指定されていますが、クエリーがアーカイブされているものは考慮されていないため、アーカイブされたクエリーが最後に取得した結果 はクリーンアップされず延々と残ってしまいます。
この挙動が意図的なものかどうかはわかりませんが、アーカイブされたクエリー結果もクリーンアップするためには、クエリーのアーカイブ処理やクリーンアップ処理周りの修正が必要になると思われます。
残ってしまったクエリー結果をどうするか
アーカイブしたクエリー結果が残ってしまった場合、設定などで簡単に回避できるものではないため、運用に影響がない場合はそのままにするのが良いと考えています。