A model representing cache configuration. | (ns metabase.models.cache-config (:require [java-time.api :as t] [medley.core :as m] [metabase.db.query :as mdb.query] [metabase.events :as events] [metabase.models.interface :as mi] [metabase.util :as u] [methodical.core :as methodical] [toucan2.core :as t2])) |
Caching is configurable for those models | (def CachingModel [:enum "root" "database" "dashboard" "question"]) |
(doto :model/CacheConfig (derive :metabase/model) (derive :hook/timestamped?)) | |
(methodical/defmethod t2/table-name :model/CacheConfig [_model] :cache_config) | |
(t2/deftransforms :model/CacheConfig {:strategy mi/transform-keyword :config mi/transform-json :state mi/transform-json}) | |
(defn- audit-caching-change! [user-id id prev new] (events/publish-event! :event/cache-config-update {:user-id user-id :model :model/CacheConfig :model-id id :details {:model (or (:model prev) (:model new)) :model-id (or (:model_id prev) (:model_id new)) :old-value (dissoc prev :model :model_id) :new-value (dissoc new :model :model_id)}})) | |
API | |
Returns root strategy, if it's defined. | (defn root-strategy [] (t2/select-one :model/CacheConfig :model "root" :model_id 0 :strategy :ttl)) |
Transform from how cache config is stored to how it's used/exposed in the API. | (defn row->config [row] (when row {:model (:model row) :model_id (:model_id row) :strategy (-> (:config row) (assoc :type (:strategy row)) (cond-> (#{:duration :schedule} (:strategy row)) (assoc :refresh_automatically (:refresh_automatically row))))})) |
Shapes | (defn card-strategy [row card] (some-> (:strategy (row->config row)) (m/assoc-some :invalidated-at (t/max (:invalidated_at row) (:cache_invalidated_at card))))) |
Transform cache config from API form into db storage form. | (defn config->row [{:keys [model model_id strategy]}] {:model model :model_id model_id :strategy (:type strategy) :config (dissoc strategy :type :refresh_automatically) :refresh_automatically (:refresh_automatically strategy)}) |
Get a list of cache configurations for given | (defn get-list [models collection id] (->> (if id (t2/select :model/CacheConfig {:where [:and [:in :model models] [:= :model_id id]]}) (t2/select :model/CacheConfig :model [:in models] {:left-join [:report_card [:and [:= :model [:inline "question"]] [:= :model_id :report_card.id] (when collection [:= :report_card.collection_id collection])] :report_dashboard [:and [:= :model [:inline "dashboard"]] [:= :model_id :report_dashboard.id] (when collection [:= :report_dashboard.collection_id collection])]] :where [:case [:= :model [:inline "question"]] [:!= :report_card.id nil] [:= :model [:inline "dashboard"]] [:!= :report_dashboard.id nil] :else true]})) (mapv row->config))) |
Store cache configuration in DB. | (defn store! [user-id {:keys [model model_id] :as config}] (t2/with-transaction [_tx] (let [data (config->row config) current (t2/select-one :model/CacheConfig :model model :model_id model_id {:for :update})] (u/prog1 (mdb.query/update-or-insert! :model/CacheConfig {:model model :model_id model_id} (constantly data)) (audit-caching-change! user-id <> current data))))) |
Delete cache configuration (possibly multiple), identified by a | (defn delete! [user-id model model-ids] (when-let [current (seq (t2/select :model/CacheConfig :model model :model_id [:in model-ids]))] (t2/delete! :model/CacheConfig :model model :model_id [:in model-ids]) (doseq [item current] (audit-caching-change! user-id (:id item) (select-keys item [:strategy :config :model :model_id]) nil)))) |
Invalidation | |
(defn- invalidate-cards [databases dashboards questions] (let [card-ids (concat questions (when (seq databases) (t2/select-fn-vec :id [:model/Card :id] :database_id [:in databases])) (when (seq dashboards) (t2/select-fn-vec :card_id [:model/DashboardCard :card_id] :dashboard_id [:in dashboards])))] (if (empty? card-ids) -1 (t2/update! :model/Card :id [:in card-ids] {:cache_invalidated_at (t/offset-date-time)})))) | |
(defn- invalidate-cache-configs [databases dashboards questions] (let [conditions (for [[k vs] [[:database databases] [:dashboard dashboards] [:question questions]] v vs] [:and [:= :model (name k)] [:= :model_id v]])] (if (empty? conditions) -1 ;; using JVM date rather than DB time since it's what are used in cache tasks (t2/query-one {:update (t2/table-name :model/CacheConfig) :set {:invalidated_at (t/offset-date-time)} :where (into [:or] conditions)})))) | |
Invalidate cache configuration. Accepts lists of ids for different types of models. If | (defn invalidate! [{:keys [databases dashboards questions with-overrides?]}] (if with-overrides? (invalidate-cards databases dashboards questions) (invalidate-cache-configs databases dashboards questions))) |