| |
|
| (ns metabase.query-processor.middleware.update-used-cards
(:require
[java-time.api :as t]
[metabase.lib.metadata :as lib.metadata]
[metabase.query-processor.schema :as qp.schema]
[metabase.query-processor.store :as qp.store]
[metabase.util.grouper :as grouper]
[metabase.util.log :as log]
[metabase.util.malli :as mu]
^{:clj-kondo/ignore [:discouraged-namespace]}
[toucan2.core :as t2])) |
|
| (set! *warn-on-reflection* true) |
|
| (def ^:private update-used-card-interval-seconds 20) |
|
| (defn- update-used-cards!*
[card-id-timestamps]
(let [card-id->timestamp (update-vals (group-by :id card-id-timestamps)
(fn [xs] (apply t/max (map :timestamp xs))))]
(log/debugf "Update last_used_at of %d cards" (count card-id->timestamp))
(try
(t2/update! :model/Card :id [:in (keys card-id->timestamp)]
{:last_used_at (into [:case]
(mapcat (fn [[id timestamp]]
[[:= :id id] [:greatest [:coalesce :last_used_at (t/offset-date-time 0)] timestamp]])
card-id->timestamp))
;; Set updated_at to its current value to prevent it from updating automatically
:updated_at :updated_at})
(catch Throwable e
(log/error e "Error updating used cards"))))) |
|
| (defonce ^:private
update-used-cards-queue
(delay
(grouper/start!
update-used-cards!*
:capacity 500
:interval (* update-used-card-interval-seconds 1000)))) |
|
| (mu/defn update-used-cards! :- ::qp.schema/qp
"Middleware that get all card-ids that were used during a query execution and updates their `last_used_at`.
Should be used after query is fully preprocessed.
Including but not limited to cards used as:
- the source card for other queries
- definition for sandbox rules
- card references in native query
- dashcard on dashboard
- alert/pulse"
[qp :- ::qp.schema/qp]
(mu/fn [query :- ::qp.schema/query
rff :- ::qp.schema/rff]
(let [now (t/offset-date-time)
rff* (fn [metadata]
(doseq [card-id (distinct (lib.metadata/invoked-ids (qp.store/metadata-provider) :metadata/card))]
(grouper/submit! @update-used-cards-queue {:id card-id
:timestamp now}))
(rff metadata))]
(qp query rff*)))) |
| | |