diff --git a/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj b/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj
index d3311b67613bfac88f4782a330d6640bb7d11de1..f969265547b7f2a2932295abb6975d757d1dc80f 100644
--- a/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj
+++ b/enterprise/backend/test/metabase_enterprise/sandbox/query_processor/middleware/row_level_restrictions_test.clj
@@ -6,21 +6,23 @@
             [medley.core :as m]
             [metabase-enterprise.sandbox.models.group-table-access-policy :refer [GroupTableAccessPolicy]]
             [metabase-enterprise.sandbox.query-processor.middleware.row-level-restrictions :as row-level-restrictions]
-            [metabase-enterprise.sandbox.test-util :as mt.tu]
             [metabase.api.common :as api]
             [metabase.driver :as driver]
+            [metabase.driver.ddl.interface :as ddl.i]
             [metabase.driver.sql.query-processor :as sql.qp]
             [metabase.mbql.normalize :as mbql.normalize]
             [metabase.mbql.util :as mbql.u]
             [metabase.models :refer [Card Collection Field Table]]
             [metabase.models.permissions :as perms]
             [metabase.models.permissions-group :as perms-group]
+            [metabase.models.persisted-info :as persisted-info]
             [metabase.query-processor :as qp]
             [metabase.query-processor.middleware.cache-test :as cache-test]
             [metabase.query-processor.middleware.permissions :as qp.perms]
             [metabase.query-processor.pivot :as qp.pivot]
             [metabase.query-processor.util :as qp.util]
             [metabase.query-processor.util.add-alias-info :as add]
+            [metabase.task.persist-refresh :as task.persist-refresh]
             [metabase.test :as mt]
             [metabase.test.data.env :as tx.env]
             [metabase.util :as u]
@@ -680,7 +682,7 @@
             (is (= [[10]]
                    (mt/rows result)))))
         (testing "Run the query with different User attributes, should not get the cached result"
-          (mt.tu/with-user-attributes :rasta {"cat" 40}
+          (mt/with-user-attributes :rasta {"cat" 40}
             ;; re-bind current user so updated attributes come in to effect
             (mt/with-test-user :rasta
               (is (= {"cat" 40}
@@ -1002,3 +1004,60 @@
             ;; this should *NOT* be cached because we're generating a nested query with sandboxing in play.
             (is (= {:cached? false, :num-rows 5}
                    (run-query)))))))))
+
+(deftest persistence-disabled-when-sandboxed
+  (mt/test-driver :postgres
+    (mt/dataset sample-dataset
+      ;; with-gtaps creates a new copy of the database. So make sure to do that before anything else. Gets really
+      ;; confusing when `(mt/id)` and friends change value halfway through the test
+      (mt/with-gtaps {:gtaps      {:products
+                                   {:remappings {:category
+                                                 ["dimension"
+                                                  [:field (mt/id :products :category)
+                                                   nil]]}}}
+                      :attributes {"category" nil}}
+        (mt/with-temporary-setting-values [:persisted-models-enabled true]
+          (mt/with-temp* [Card [model {:dataset       true
+                                       :dataset_query (mt/mbql-query
+                                                       products
+                                                       ;; note does not include the field we have to filter on. No way
+                                                       ;; to use the sandbox filter on the cached table
+                                                       {:fields [$id $price]})}]]
+            ;; persist model
+            (ddl.i/check-can-persist (mt/db))
+            (persisted-info/ready-database! (mt/id))
+            (#'task.persist-refresh/refresh-tables!
+             (mt/id)
+             (var-get #'task.persist-refresh/dispatching-refresher))
+            (let [persisted-info (db/select-one 'PersistedInfo
+                                                :database_id (mt/id)
+                                                :card_id (:id model))]
+              (is (= "persisted" (:state persisted-info))
+                  "Model failed to persist")
+              (is (string? (:table_name persisted-info)))
+              (let [query         {:type     :query
+                                   ;; just generate a select count(*) from card__<id>
+                                   :query    {:aggregation  [:count]
+                                              :source-table (str "card__" (:id model))}
+                                   :database (mt/id)}
+                    regular-result (mt/with-test-user :crowberto
+                                     (qp/process-query query))
+                    sandboxed-result (mt/with-user-attributes :rasta {"category" "Gizmo"}
+                                       (mt/with-test-user :rasta
+                                         (qp/process-query query)))]
+                (testing "Unsandboxed"
+                  (testing "Sees full result set"
+                    (is (= 200 (-> regular-result mt/rows ffirst))
+                        "Expected 200 product results from cached, non-sandboxed results"))
+                  (testing "Uses the cache table"
+                    (is (str/includes? (-> regular-result :data :native_form :query)
+                                       (:table_name persisted-info))
+                        "Did not use the persisted model cache")))
+                (testing "Sandboxed"
+                  (testing "sees partial result"
+                    (is (= 51 (-> sandboxed-result mt/rows ffirst))
+                        "Sandboxed user got whole results instead of filtered"))
+                  (testing "Does not use the cache table"
+                    (is (not (str/includes? (-> sandboxed-result :data :native_form :query)
+                                            (:table_name persisted-info)))
+                        "Erroneously used the persisted model cache")))))))))))
diff --git a/src/metabase/query_processor/middleware/fetch_source_query.clj b/src/metabase/query_processor/middleware/fetch_source_query.clj
index 32c741cf9e20eedffb96e8317ee738b2987e617f..f697cd11b946dbafd062d690c6ff9db001d754fc 100644
--- a/src/metabase/query_processor/middleware/fetch_source_query.clj
+++ b/src/metabase/query_processor/middleware/fetch_source_query.clj
@@ -125,6 +125,12 @@
               :table
               (:table_name card)))))
 
+(defn- segmented-user?
+  []
+  (if-let [segmented? (resolve 'metabase-enterprise.sandbox.api.util/segmented-user?)]
+    (segmented?)
+    false))
+
 (s/defn card-id->source-query-and-metadata :- SourceQueryAndMetadata
   "Return the source query info for Card with `card-id`. Pass true as the optional second arg `log?` to enable
   logging. (The circularity check calls this and will print more than desired)"
@@ -142,8 +148,8 @@
                   (db/do-post-select Card)
                   (db/do-post-select PersistedInfo)
                   first)
-           (throw (ex-info (tru "Card {0} does not exist." card-id)
-                           {:card-id card-id})))
+             (throw (ex-info (tru "Card {0} does not exist." card-id)
+                             {:card-id card-id})))
 
          {{mbql-query                   :query
            database-id                  :database
@@ -160,6 +166,7 @@
                          (= (:query_hash card) (persisted-info/query-hash (:dataset_query card)))
                          (= (:definition card) (persisted-info/metadata->definition (:result_metadata card)
                                                                                     (:table_name card)))
+                         (not (segmented-user?))
                          (= (:state card) "persisted"))
 
          source-query (cond
diff --git a/test/metabase/task/persist_refresh_test.clj b/test/metabase/task/persist_refresh_test.clj
index f938fff35408de4ded1940bb9c0fb44f3df23c51..49de4164d2ebf10de109605b179e0f29b3e578c1 100644
--- a/test/metabase/task/persist_refresh_test.clj
+++ b/test/metabase/task/persist_refresh_test.clj
@@ -9,7 +9,7 @@
             [metabase.query-processor :as qp]
             [metabase.query-processor.interface :as qp.i]
             [metabase.query-processor.timezone :as qp.timezone]
-            [metabase.task.persist-refresh :as pr]
+            [metabase.task.persist-refresh :as task.persist-refresh]
             [metabase.test :as mt]
             [metabase.util :as u]
             [potemkin.types :as p]
@@ -28,48 +28,48 @@
 (deftest cron-schedule-test
   (testing "creates schedule per hour when less than 24 hours"
     (is (= "0 0 0/8 * * ? *"
-           (schedule-string (#'pr/cron-schedule "0 0 0/8 * * ? *"))))
+           (schedule-string (#'task.persist-refresh/cron-schedule "0 0 0/8 * * ? *"))))
     (testing "when anchored"
       (is (= "0 30 1/8 * * ? *"
-             (schedule-string (#'pr/cron-schedule "0 30 1/8 * * ? *"))))))
+             (schedule-string (#'task.persist-refresh/cron-schedule "0 30 1/8 * * ? *"))))))
   (testing "creates schedule string per day when 24 hours"
     (is (= "0 0 0 * * ? *"
-           (schedule-string (#'pr/cron-schedule "0 0 0 * * ? *"))))
+           (schedule-string (#'task.persist-refresh/cron-schedule "0 0 0 * * ? *"))))
     (testing "when anchored"
       (is (= "0 30 1 * * ? *"
-             (schedule-string (#'pr/cron-schedule "0 30 1 * * ? *")))))))
+             (schedule-string (#'task.persist-refresh/cron-schedule "0 30 1 * * ? *")))))))
 
 (deftest trigger-job-info-test
   (testing "Database refresh trigger"
-    (let [tggr (#'pr/database-trigger {:id 1} "0 0 0/5 * * ? *")]
+    (let [tggr (#'task.persist-refresh/database-trigger {:id 1} "0 0 0/5 * * ? *")]
       (is (= {"db-id" 1 "type" "database"}
              (qc/from-job-data (.getJobDataMap tggr))))
       (is (= "0 0 0/5 * * ? *"
              (schedule-string tggr)))
       (is (= "metabase.task.PersistenceRefresh.database.trigger.1"
              (.. tggr getKey getName))))
-    (let [tggr (#'pr/database-trigger {:id 1} "0 0 0 * * ? *")]
+    (let [tggr (#'task.persist-refresh/database-trigger {:id 1} "0 0 0 * * ? *")]
       (is (= {"db-id" 1 "type" "database"}
              (qc/from-job-data (.getJobDataMap tggr))))
       (is (= "0 0 0 * * ? *"
              (schedule-string tggr))))
     (testing "in report timezone UTC"
       (mt/with-temporary-setting-values [report-timezone "UTC"]
-        (let [tggr (#'pr/database-trigger {:id 1} "0 0 0/5 * * ? *")]
+        (let [tggr (#'task.persist-refresh/database-trigger {:id 1} "0 0 0/5 * * ? *")]
           (is (= "UTC"
                  (.. tggr getTimeZone getID))))))
     (testing "in report timezone LA"
       (mt/with-temporary-setting-values [report-timezone "America/Los_Angeles"]
-        (let [tggr (#'pr/database-trigger {:id 1} "0 0 0/5 * * ? *")]
+        (let [tggr (#'task.persist-refresh/database-trigger {:id 1} "0 0 0/5 * * ? *")]
           (is (= "America/Los_Angeles"
                  (.. tggr getTimeZone getID))))))
     (testing "in system timezone"
       (mt/with-temporary-setting-values [report-timezone nil]
-        (let [tggr (#'pr/database-trigger {:id 1} "0 0 0/5 * * ? *")]
+        (let [tggr (#'task.persist-refresh/database-trigger {:id 1} "0 0 0/5 * * ? *")]
           (is (= (qp.timezone/system-timezone-id)
                  (.. tggr getTimeZone getID)))))))
   (testing "Individual refresh trigger"
-    (let [tggr (#'pr/individual-trigger {:card_id 5 :id 1})]
+    (let [tggr (#'task.persist-refresh/individual-trigger {:card_id 5 :id 1})]
       (is (= {"persisted-id" 1 "type" "individual"}
              (qc/from-job-data (.getJobDataMap tggr))))
       (is (= "metabase.task.PersistenceRefresh.individual.trigger.1"
@@ -80,15 +80,15 @@
   (let [ids  (into #{} (map u/the-id dbs))]
     (m/map-vals
      #(select-keys % [:data :schedule :key])
-     (select-keys (pr/job-info-by-db-id) ids))))
+     (select-keys (task.persist-refresh/job-info-by-db-id) ids))))
 
 (deftest reschedule-refresh-test
   (mt/with-temp-scheduler
     (mt/with-temp* [Database [db-1 {:options {:persist-models-enabled true}}]
                     Database [db-2 {:options {:persist-models-enabled true}}]]
-      (#'pr/job-init!)
+      (#'task.persist-refresh/job-init!)
       (mt/with-temporary-setting-values [persisted-model-refresh-cron-schedule "0 0 0/4 * * ? *"]
-        (pr/reschedule-refresh!)
+        (task.persist-refresh/reschedule-refresh!)
         (is (= {(u/the-id db-1) {:data {"db-id" (u/the-id db-1) "type" "database"}
                                  :schedule "0 0 0/4 * * ? *"
                                  :key (format "metabase.task.PersistenceRefresh.database.trigger.%d" (u/the-id db-1))}
@@ -97,7 +97,7 @@
                                  :key (format "metabase.task.PersistenceRefresh.database.trigger.%d" (u/the-id db-2))}}
                (job-info db-1 db-2))))
       (mt/with-temporary-setting-values [persisted-model-refresh-cron-schedule "0 0 0/8 * * ? *"]
-        (pr/reschedule-refresh!)
+        (task.persist-refresh/reschedule-refresh!)
         (is (= {(u/the-id db-1) {:data {"db-id" (u/the-id db-1) "type" "database"}
                                  :schedule "0 0 0/8 * * ? *"
                                  :key (format "metabase.task.PersistenceRefresh.database.trigger.%d" (u/the-id db-1))}
@@ -106,7 +106,7 @@
                                  :key (format "metabase.task.PersistenceRefresh.database.trigger.%d" (u/the-id db-2))}}
                (job-info db-1 db-2))))
       (mt/with-temporary-setting-values [persisted-model-refresh-cron-schedule "0 30 1/8 * * ? *"]
-        (pr/reschedule-refresh!)
+        (task.persist-refresh/reschedule-refresh!)
         (is (= {(u/the-id db-1) {:data {"db-id" (u/the-id db-1) "type" "database"}
                                  :schedule "0 30 1/8 * * ? *"
                                  :key (format "metabase.task.PersistenceRefresh.database.trigger.%d" (u/the-id db-1))}
@@ -129,12 +129,12 @@
                     PersistedInfo [_punmodeled {:card_id (u/the-id unmodeled) :database_id (u/the-id db)}]]
       (testing "Calls refresh on each persisted-info row"
         (let [card-ids (atom #{})
-              test-refresher (reify pr/Refresher
+              test-refresher (reify task.persist-refresh/Refresher
                                (refresh! [_ _database _definition card]
                                  (swap! card-ids conj (:id card))
                                  {:state :success})
                                (unpersist! [_ _database _persisted-info]))]
-          (#'pr/refresh-tables! (u/the-id db) test-refresher)
+          (#'task.persist-refresh/refresh-tables! (u/the-id db) test-refresher)
           (testing "Does not refresh archived cards or cards no longer models."
             (is (= #{(u/the-id model1) (u/the-id model2)} @card-ids)))
           (is (partial= {:task "persist-refresh"
@@ -145,7 +145,7 @@
                                        {:order-by [[:id :desc]]})))))
       (testing "Handles errors and continues"
         (let [call-count (atom 0)
-              test-refresher (reify pr/Refresher
+              test-refresher (reify task.persist-refresh/Refresher
                                (refresh! [_ _database _definition _card]
                                  (swap! call-count inc)
                                  ;; throw on first persist
@@ -153,7 +153,7 @@
                                    (throw (ex-info "DBs are risky" {:ka :boom})))
                                  {:state :success})
                                (unpersist! [_ _database _persisted-info]))]
-          (#'pr/refresh-tables! (u/the-id db) test-refresher)
+          (#'task.persist-refresh/refresh-tables! (u/the-id db) test-refresher)
           (is (= 2 @call-count))
           (is (partial= {:task "persist-refresh"
                          :task_details {:success 1 :error 1}}
@@ -173,17 +173,17 @@
                                                 ;; need an "old enough" state change
                                                 :state_change_at (t/minus (t/local-date-time) (t/hours 2))}]]
         (let [called-on (atom #{})
-              test-refresher (reify pr/Refresher
+              test-refresher (reify task.persist-refresh/Refresher
                                (refresh! [_ _ _ _]
                                  (is false "refresh! called on a model that should not be refreshed"))
                                (unpersist! [_ _database persisted-info]
                                  (swap! called-on conj (u/the-id persisted-info))))]
           (testing "Query finds deletabable, archived, and unmodeled persisted infos"
-            (let [queued-for-deletion (into #{} (map :id) (#'pr/deletable-models))]
+            (let [queued-for-deletion (into #{} (map :id) (#'task.persist-refresh/deletable-models))]
               (doseq [deletable-persisted [deletable punmodeled parchived]]
                 (is (contains? queued-for-deletion (u/the-id deletable-persisted))))))
           ;; we manually pass in the deleteable ones to not catch others in a running instance
-          (#'pr/prune-deletables! test-refresher [deletable parchived punmodeled])
+          (#'task.persist-refresh/prune-deletables! test-refresher [deletable parchived punmodeled])
           ;; don't assert equality if there are any deletable in the app db
           (doseq [deletable-persisted [deletable punmodeled parchived]]
             (is (contains? @called-on (u/the-id deletable-persisted))))
@@ -217,7 +217,7 @@
                     [persisted-info-id] (persisted-info/ready-unpersisted-models! (mt/id))]
                 ;; Persist the model
                 (ddl.i/check-can-persist (db/select-one Database :id (mt/id)))
-                (#'pr/refresh-individual! persisted-info-id (var-get #'pr/dispatching-refresher))
+                (#'task.persist-refresh/refresh-individual! persisted-info-id (var-get #'task.persist-refresh/dispatching-refresher))
                  ;; Check the number of rows is the same after persisting
                 (let [query-on-top {:database (mt/id)
                                     :type     :query