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))) |