From 8eb25e5b14d6c70ca39229948a5a85596e142324 Mon Sep 17 00:00:00 2001
From: Oleksandr Yakushev <alex@bytopia.org>
Date: Thu, 5 Sep 2024 20:25:48 +0300
Subject: [PATCH] perf: Miscellaneous card rendering improvements (#47679)

* perf: Optimize reading rows into vectors in JDBC driver

* perf: Use direct interop when computing with-time-zone-same-instant
---
 src/metabase/driver/sql_jdbc/execute.clj |  7 ++++---
 src/metabase/util/date_2.clj             | 12 +++++-------
 src/metabase/util/performance.clj        | 11 +++++++++++
 3 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/src/metabase/driver/sql_jdbc/execute.clj b/src/metabase/driver/sql_jdbc/execute.clj
index 3dfd8cf0ce4..0d69910de62 100644
--- a/src/metabase/driver/sql_jdbc/execute.clj
+++ b/src/metabase/driver/sql_jdbc/execute.clj
@@ -30,6 +30,7 @@
    [metabase.util.i18n :refer [tru]]
    [metabase.util.log :as log]
    [metabase.util.malli :as mu]
+   [metabase.util.performance :as perf]
    [potemkin :as p])
   (:import
    (java.sql Connection JDBCType PreparedStatement ResultSet ResultSetMetaData SQLFeatureNotSupportedException
@@ -634,11 +635,11 @@
   "Returns a thunk that can be called repeatedly to get the next row in the result set, using appropriate methods to
   fetch each value in the row. Returns `nil` when the result set has no more rows."
   [driver ^ResultSet rs ^ResultSetMetaData rsmeta]
-  (let [fns (for [i (column-range rsmeta)]
-              (read-column-thunk driver rs rsmeta (long i)))]
+  (let [fns (mapv #(read-column-thunk driver rs rsmeta (long %))
+                  (column-range rsmeta))]
     (log-readers driver rsmeta fns)
     (let [thunk (if (seq fns)
-                  (apply juxt fns)
+                  (perf/juxt* fns)
                   (constantly []))]
       (fn row-thunk* []
         (when (.next rs)
diff --git a/src/metabase/util/date_2.clj b/src/metabase/util/date_2.clj
index ab5c4ea6ece..e6a341e33d1 100644
--- a/src/metabase/util/date_2.clj
+++ b/src/metabase/util/date_2.clj
@@ -514,6 +514,8 @@
     converts it to the corresponding offset/zoned type; for offset/zoned types, this applies an appropriate timezone
     shift."))
 
+(def ^:private local-time-0 (t/local-time 0))
+
 (extend-protocol WithTimeZoneSameInstant
   ;; convert to a OffsetTime with no offset (UTC); the OffsetTime method impl will apply the zone shift.
   LocalTime
@@ -526,15 +528,11 @@
 
   LocalDate
   (with-time-zone-same-instant [t zone-id]
-    (t/offset-date-time t (t/local-time 0) zone-id))
-
-  LocalDate
-  (with-time-zone-same-instant [t zone-id]
-    (t/offset-date-time t (t/local-time 0) zone-id))
+    (with-time-zone-same-instant (LocalDateTime/of t local-time-0) zone-id))
 
   LocalDateTime
-  (with-time-zone-same-instant [t zone-id]
-    (t/offset-date-time t zone-id))
+  (with-time-zone-same-instant [t ^java.time.ZoneId zone-id]
+    (OffsetDateTime/of t (.getOffset (.getRules zone-id) t)))
 
   ;; instants are always normalized to UTC, so don't make any changes here. If you want to format in a different zone,
   ;; convert to an OffsetDateTime or ZonedDateTime first.
diff --git a/src/metabase/util/performance.clj b/src/metabase/util/performance.clj
index 58786746ac1..3deb78bef9d 100644
--- a/src/metabase/util/performance.clj
+++ b/src/metabase/util/performance.clj
@@ -69,3 +69,14 @@
    (persistent! (reduce #(conj! %1 (f %2 %3 %4)) (transient [])  coll1 coll2 coll3)))
   ([f coll1 coll2 coll3 coll4]
    (persistent! (reduce #(conj! %1 (f %2 %3 %4 %5)) (transient []) coll1 coll2 coll3 coll4))))
+
+(defn juxt*
+  "Like `clojure.core/juxt`, but accepts a list of functions instead of varargs. Uses more efficient mapping."
+  [fns]
+  (let [fns (vec fns)]
+    (fn
+      ([] (mapv #(%) fns))
+      ([x] (mapv #(% x) fns))
+      ([x y] (mapv #(% x y) fns))
+      ([x y z] (mapv #(% x y z) fns))
+      ([x y z & args] (mapv #(apply % x y z args) fns)))))
-- 
GitLab