Tasks for truncating audit-related tables, particularly | (ns metabase.task.truncate-audit-tables (:require [clojurewerkz.quartzite.jobs :as jobs] [clojurewerkz.quartzite.schedule.cron :as cron] [clojurewerkz.quartzite.triggers :as triggers] [java-time.api :as t] [metabase.config :as config] [metabase.db :as mdb] [metabase.models.setting :as setting :refer [defsetting]] [metabase.models.task-history :as task-history] [metabase.plugins.classloader :as classloader] [metabase.premium-features.core :refer [defenterprise]] [metabase.task :as task] [metabase.util.i18n :refer [deferred-tru]] [metabase.util.log :as log] [toucan2.core :as t2])) |
(set! *warn-on-reflection* true) | |
Load EE implementation if available | (when config/ee-available? (classloader/require 'metabase-enterprise.task.truncate-audit-tables)) |
Minimum allowed value for | (def min-retention-days 30) |
Default value for | (def default-retention-days 720) |
Logs a warning that the value for | (defn log-minimum-value-warning
[env-var-value]
(log/warnf "MB_AUDIT_MAX_RETENTION_DAYS is set to %d; using the minimum value of %d instead."
env-var-value
min-retention-days)) |
(defsetting audit-max-retention-days
(deferred-tru "Number of days to retain data in audit-related tables. Minimum value is 30; set to 0 to retain data indefinitely.")
:visibility :internal
:setter :none
:audit :never
:getter (fn []
(let [env-var-value (setting/get-value-of-type :integer :audit-max-retention-days)]
(cond
(nil? env-var-value)
default-retention-days
;; Treat 0 as an alias for infinity
(zero? env-var-value)
##Inf
(< env-var-value min-retention-days)
(do
(log-minimum-value-warning env-var-value)
min-retention-days)
:else
env-var-value)))
:doc "Sets the maximum number of days Metabase preserves rows for the following application database tables:
- `query_execution`
- `audit_log`
- `view_log`
Twice a day, Metabase will delete rows older than this threshold. The minimum value is 30 days (Metabase will treat entered values of 1 to 29 the same as 30).
If set to 0, Metabase will keep all rows.") | |
(defsetting audit-table-truncation-batch-size (deferred-tru "Batch size to use for deletion of old rows for audit-related tables (like query_execution). Can be only set as an environment variable.") :visibility :internal :setter :none :type :integer :default 50000 :audit :never :export? false) | |
(defn- truncate-table-batched!
[table-name time-column]
(t2/query-one
(case (mdb/db-type)
(:postgres :h2)
{:delete-from (keyword table-name)
:where [:in
:id
{:select [:id]
:from (keyword table-name)
:where [:<=
(keyword time-column)
(t/minus (t/offset-date-time) (t/days (audit-max-retention-days)))]
:order-by [[:id :asc]]
:limit (audit-table-truncation-batch-size)}]}
(:mysql :mariadb)
{:delete-from (keyword table-name)
:where [:<=
(keyword time-column)
(t/minus (t/offset-date-time) (t/days (audit-max-retention-days)))]
:limit (audit-table-truncation-batch-size)}))) | |
Given a model, deletes all rows older than the configured threshold | (defn- truncate-table!
[model time-column]
(when-not (infinite? (audit-max-retention-days))
(let [table-name (name (t2/table-name model))]
(try
(log/infof "Cleaning up %s table" table-name)
(loop [total-rows-deleted 0]
(let [batch-rows-deleted (truncate-table-batched! table-name time-column)]
;; Only try to delete another batch if the last batch was full
(if (= batch-rows-deleted (audit-table-truncation-batch-size))
(recur (+ total-rows-deleted (long batch-rows-deleted)))
(if (not= total-rows-deleted 0)
(log/infof "%s cleanup successful, %d rows were deleted" table-name total-rows-deleted)
(log/infof "%s cleanup successful, no rows were deleted" table-name)))))
(catch Throwable e
(log/errorf e "%s cleanup failed" table-name)))))) |
List of models to truncate. OSS implementation only truncates | (defenterprise audit-models-to-truncate
metabase-enterprise.task.truncate-audit-tables
[]
[{:model :model/QueryExecution :timestamp-col :started_at}]) |
(defn- truncate-audit-tables!
[]
(run!
(fn [{:keys [model timestamp-col]}]
(task-history/with-task-history {:task "task-history-cleanup"}
(truncate-table! model timestamp-col)))
(audit-models-to-truncate))) | |
Triggers the removal of | (jobs/defjob TruncateAuditTables [_] (truncate-audit-tables!)) |
(def ^:private truncate-audit-tables-job-key "metabase.task.truncate-audit-tables.job") (def ^:private truncate-audit-tables-trigger-key "metabase.task.truncate-audit-tables.trigger") (def ^:private truncate-audit-tables-cron "0 0 */12 * * ? *") ;; Run every 12 hours | |
Run every 12 hours | |
(defmethod task/init! ::TruncateAuditTables [_]
(let [job (jobs/build
(jobs/of-type TruncateAuditTables)
(jobs/with-identity (jobs/key truncate-audit-tables-job-key)))
trigger (triggers/build
(triggers/with-identity (triggers/key truncate-audit-tables-trigger-key))
(triggers/start-now)
(triggers/with-schedule
(cron/schedule
(cron/cron-schedule truncate-audit-tables-cron)
(cron/with-misfire-handling-instruction-do-nothing))))]
(task/schedule-task! job trigger))) | |