From 83f948ff6052841ef1f72832f7e17bb44ed60f97 Mon Sep 17 00:00:00 2001 From: dpsutton <dan@dpsutton.com> Date: Tue, 22 Jun 2021 17:28:44 -0500 Subject: [PATCH] Move *ignore-cached-results* to middleware (#16695) * Move *ignore-cached-results* to middleware Had tried to adjust the binding to work, but the binding ended before all of the bound functions and such were submitted to work, so when that code actually executed the binding was no longer in place. Annoying and it should be in middleware anyways * Remove now unused import (cache became keyword in middleware not binding) --- .dir-locals.el | 1 + src/metabase/api/card.clj | 50 ++++++++++--------- .../query_processor/middleware/cache.clj | 14 ++---- .../query_processor/middleware/cache_test.clj | 20 +++----- 4 files changed, 39 insertions(+), 46 deletions(-) diff --git a/.dir-locals.el b/.dir-locals.el index 009b7d29608..72cadc8d07d 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -36,6 +36,7 @@ (p.types/deftype+ '(2 nil nil (:defn))) (p/def-map-type '(2 nil nil (:defn))) (p.types/defrecord+ '(2 nil nil (:defn))) + (qp.streaming/streaming-response 1) (prop/for-all 1) (tools.macro/macrolet '(1 (:defn)))))) (clojure-indent-style . always-align) diff --git a/src/metabase/api/card.clj b/src/metabase/api/card.clj index 0fb644ee563..1bdf01f69cf 100644 --- a/src/metabase/api/card.clj +++ b/src/metabase/api/card.clj @@ -25,7 +25,6 @@ [metabase.public-settings :as public-settings] [metabase.query-processor :as qp] [metabase.query-processor.async :as qp.async] - [metabase.query-processor.middleware.cache :as cache] [metabase.query-processor.middleware.constraints :as constraints] [metabase.query-processor.middleware.permissions :as qp.perms] [metabase.query-processor.middleware.results-metadata :as results-metadata] @@ -615,21 +614,23 @@ be returned as the result of an API endpoint fn. Will throw an Exception if preconditions (such as read perms) are not met before returning the `StreamingResponse`." [card-id export-format - & {:keys [parameters constraints context dashboard-id middleware qp-runner run] + & {:keys [parameters constraints context dashboard-id middleware qp-runner run ignore_cache] :or {constraints constraints/default-query-constraints context :question - qp-runner qp/process-query-and-save-execution! - ;; param `run` can be used to control how the query is ran, e.g. if you need to - ;; customize the `context` passed to the QP - run (^:once fn* [query info] - (qp.streaming/streaming-response [context export-format] - (binding [qp.perms/*card-id* card-id] - (qp-runner query info context))))}}] + qp-runner qp/process-query-and-save-execution!}}] {:pre [(u/maybe? sequential? parameters)]} - (let [card (api/read-check (Card card-id)) + (let [run (or run + ;; param `run` can be used to control how the query is ran, e.g. if you need to + ;; customize the `context` passed to the QP + (^:once fn* [query info] + (qp.streaming/streaming-response [context export-format] + (binding [qp.perms/*card-id* card-id] + (qp-runner query info context))))) + card (api/read-check (Card card-id)) query (-> (assoc (query-for-card card parameters constraints middleware) :async? true) - (assoc-in [:middleware :js-int-to-string?] true)) + (update :middleware merge {:js-int-to-string? true + :ignore-cached-results? ignore_cache})) info {:executed-by api/*current-user-id* :context context :card-id card-id @@ -641,8 +642,7 @@ "Run the query associated with a Card." [card-id :as {{:keys [parameters ignore_cache], :or {ignore_cache false}} :body}] {ignore_cache (s/maybe s/Bool)} - (binding [cache/*ignore-cached-results* ignore_cache] - (run-query-for-card-async card-id :api, :parameters parameters))) + (run-query-for-card-async card-id :api, :parameters parameters, :ignore_cache ignore_cache)) (api/defendpoint ^:streaming POST "/:card-id/query/:export-format" "Run the query associated with a Card, and return its results as a file in the specified format. Note that this @@ -650,15 +650,15 @@ [card-id export-format :as {{:keys [parameters]} :params}] {parameters (s/maybe su/JSONString) export-format dataset-api/ExportFormat} - (binding [cache/*ignore-cached-results* true] - (run-query-for-card-async - card-id export-format - :parameters (json/parse-string parameters keyword) - :constraints nil - :context (dataset-api/export-format->context export-format) - :middleware {:skip-results-metadata? true - :format-rows? false - :js-int-to-string? false}))) + (run-query-for-card-async + card-id export-format + :parameters (json/parse-string parameters keyword) + :constraints nil + :context (dataset-api/export-format->context export-format) + :middleware {:skip-results-metadata? true + :ignore-cached-results? true + :format-rows? false + :js-int-to-string? false})) ;;; ----------------------------------------------- Sharing is Caring ------------------------------------------------ @@ -718,7 +718,9 @@ [card-id :as {{:keys [parameters ignore_cache] :or {ignore_cache false}} :body}] {ignore_cache (s/maybe s/Bool)} - (binding [cache/*ignore-cached-results* ignore_cache] - (run-query-for-card-async card-id :api, :parameters parameters, :qp-runner qp.pivot/run-pivot-query))) + (run-query-for-card-async card-id :api + :parameters parameters, + :qp-runner qp.pivot/run-pivot-query + :ignore_cache ignore_cache)) (api/define-routes) diff --git a/src/metabase/query_processor/middleware/cache.clj b/src/metabase/query_processor/middleware/cache.clj index 07b63000251..d0ada61d46c 100644 --- a/src/metabase/query_processor/middleware/cache.clj +++ b/src/metabase/query_processor/middleware/cache.clj @@ -33,12 +33,6 @@ [initial-metadata row-1 row-2 ... row-n final-metadata]" 3) -;; TODO - Why not make this an option in the query itself? :confused: -(def ^:dynamic ^Boolean *ignore-cached-results* - "Should we force the query to run, ignoring cached results even if they're available? - Setting this to `true` will run the query again and will still save the updated results." - false) - (def ^:dynamic *backend* "Current cache backend. Dynamically rebindable primary for test purposes." (i/cache-backend (config/config-kw :mb-qp-cache-backend))) @@ -145,9 +139,9 @@ (defn- maybe-reduce-cached-results "Reduces cached results if there is a hit. Otherwise, returns `::miss` directly." - [query-hash max-age-seconds rff context] + [ignore-cache? query-hash max-age-seconds rff context] (try - (or (when-not *ignore-cached-results* + (or (when-not ignore-cache? (log/tracef "Looking for cached results for query with hash %s younger than %s\n" (pr-str (i/short-hex-hash query-hash)) (u/format-seconds max-age-seconds)) (i/with-cached-results *backend* query-hash max-age-seconds [is] @@ -173,11 +167,11 @@ ;;; --------------------------------------------------- Middleware --------------------------------------------------- (defn- run-query-with-cache - [qp {:keys [cache-ttl], :as query} rff context] + [qp {:keys [cache-ttl middleware], :as query} rff context] ;; TODO - Query will already have `info.hash` if it's a userland query. I'm not 100% sure it will be the same hash, ;; because this is calculated after normalization, instead of before (let [query-hash (qputil/query-hash query) - result (maybe-reduce-cached-results query-hash cache-ttl rff context)] + result (maybe-reduce-cached-results (:ignore-cached-results? middleware) query-hash cache-ttl rff context)] (when (= result ::miss) (let [start-time-ms (System/currentTimeMillis)] (log/trace "Running query and saving cached results (if eligible)...") diff --git a/test/metabase/query_processor/middleware/cache_test.clj b/test/metabase/query_processor/middleware/cache_test.clj index e5ba388a007..0d8e2bd7f4f 100644 --- a/test/metabase/query_processor/middleware/cache_test.clj +++ b/test/metabase/query_processor/middleware/cache_test.clj @@ -223,24 +223,20 @@ (run-query))))))) (deftest ignore-cached-results-test - (testing "check that *ignore-cached-results* is respected when returning results..." + (testing "check that :ignore-cached-results? in middleware is respected when returning results..." (with-mock-cache [save-chan] - (run-query) + (run-query :middleware {:ignore-cached-results? false}) (mt/wait-for-result save-chan) - (binding [cache/*ignore-cached-results* true] - (is (= :not-cached - (run-query))))))) + (is (= :not-cached + (run-query :middleware {:ignore-cached-results? true})))))) (deftest ignore-cached-results-should-still-save-test (testing "...but if it's set those results should still be cached for next time." (with-mock-cache [save-chan] - (binding [cache/*ignore-cached-results* true] - (is (= true - (cacheable?))) - (run-query) - (mt/wait-for-result save-chan)) - (is (= :cached - (run-query)))))) + (is (= true (cacheable?))) + (run-query :middleware {:ignore-cached-results? true}) + (mt/wait-for-result save-chan) + (is (= :cached (run-query)))))) (deftest min-ttl-test (testing "if the cache takes less than the min TTL to execute, it shouldn't be cached" -- GitLab