Jelajahi Sumber

perf: faster db validator

1. remove slow postwalk on big nested schema vector, use dynamic
var *db-for-validate-fn* instead
2. make static validator and explainer, instead of constructing them
every time
rcmerci 10 bulan lalu
induk
melakukan
a9642ed66d

+ 8 - 12
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -1,7 +1,6 @@
 (ns logseq.db.frontend.malli-schema
   "Malli schemas and fns for logseq.db.frontend.*"
-  (:require [clojure.walk :as walk]
-            [clojure.set :as set]
+  (:require [clojure.set :as set]
             [clojure.string :as string]
             [logseq.db.frontend.schema :as db-schema]
             [logseq.db.frontend.property.type :as db-property-type]
@@ -98,14 +97,6 @@
           ;; also valid if value is empty-placeholder
           (empty-placeholder-value? db property property-val)))))
 
-(defn update-properties-in-schema
-  "Needs to be called on the DB schema to add the datascript db to it"
-  [db-schema db]
-  (walk/postwalk (fn [e]
-                   (let [meta' (meta e)]
-                     (if (:add-db meta') (partial e db) e)))
-                 db-schema))
-
 (def required-properties
   "Set of properties required by a schema and that are validated directly in a schema instead
    of validate-property-value"
@@ -197,6 +188,11 @@
 ;; ==================
 ;; These schemas should be data vars to remain as simple and reusable as possible
 
+
+(def ^:dynamic *db-for-validate-fns*
+  "Used by validate-fns which need db as input"
+  nil)
+
 (def property-tuple
   "A tuple of a property map and a property value. This schema
    has 1 metadata hook which is used to inject a datascript db later"
@@ -205,8 +201,8 @@
    (map (fn [[prop-type value-schema]]
           [prop-type
            (let [schema-fn (if (vector? value-schema) (last value-schema) value-schema)]
-             [:fn (with-meta (fn [db tuple]
-                               (validate-property-value db schema-fn tuple)) {:add-db true})])])
+             [:fn (fn [tuple]
+                    (validate-property-value *db-for-validate-fns* schema-fn tuple))])])
         db-property-type/built-in-validation-schemas)))
 
 (def block-properties

+ 31 - 28
deps/db/src/logseq/db/frontend/validate.cljs

@@ -2,19 +2,22 @@
   "Validate frontend db for DB graphs"
   (:require [datascript.core :as d]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
-            [malli.util :as mu]
             [malli.core :as m]
-            [malli.error :as me]))
+            [malli.error :as me]
+            [malli.util :as mu]))
 
-(defn update-schema
-  "Updates the db schema to add a datascript db for property validations
-   and to optionally close maps"
-  [db-schema db {:keys [closed-schema?]}]
-  (cond-> db-schema
-    true
-    (db-malli-schema/update-properties-in-schema db)
-    closed-schema?
-    mu/closed-schema))
+(def ^:private db-schema-validator (m/validator db-malli-schema/DB))
+(def ^:private db-schema-explainer (m/explainer db-malli-schema/DB))
+(def ^:private closed-db-schema-validator (m/validator (mu/closed-schema db-malli-schema/DB)))
+(def ^:private closed-db-schema-explainer (m/explainer (mu/closed-schema db-malli-schema/DB)))
+
+(defn- get-schema-validator
+  [closed-schema?]
+  (if closed-schema? closed-db-schema-validator db-schema-validator))
+
+(defn- get-schema-explainer
+  [closed-schema?]
+  (if closed-schema? closed-db-schema-explainer db-schema-explainer))
 
 (defn validate-ents-before-after!
   [changed-ids db-before db-after tx-data tx-meta]
@@ -48,25 +51,26 @@
                          (assoc m :db/id db-id))
                        (db-malli-schema/datoms->entity-maps tx-datoms {:entity-fn #(d/entity db-after %)}))
         ent-maps (db-malli-schema/update-properties-in-ents db-after ent-maps*)
-        db-schema (update-schema db-malli-schema/DB db-after validate-options)
-        invalid-ent-maps (remove
-                          ;; remove :db/id as it adds needless declarations to schema
-                          #(m/validate db-schema [(dissoc % :db/id)])
-                          ent-maps)]
-    (js/console.log "changed eids:" changed-ids tx-meta)
-    (if (seq invalid-ent-maps)
-      (do
-        (js/console.error "Invalid datascript entities detected amongst changed entity ids:" changed-ids)
-        (doseq [m invalid-ent-maps]
+        validator (get-schema-validator (:closed-schema? validate-options))]
+    (binding [db-malli-schema/*db-for-validate-fns* db-after]
+      (let [invalid-ent-maps (remove
+                              ;; remove :db/id as it adds needless declarations to schema
+                              #(validator [(dissoc % :db/id)])
+                              ent-maps)]
+        (js/console.log "changed eids:" changed-ids tx-meta)
+        (if (seq invalid-ent-maps)
+          (let [explainer (get-schema-explainer (:closed-schema? validate-options))]
+            (js/console.error "Invalid datascript entities detected amongst changed entity ids:" changed-ids)
+            (doseq [m invalid-ent-maps]
 
-          (prn {:entity-map m
-                :errors (me/humanize (m/explain db-schema [m]))})
+              (prn {:entity-map m
+                    :errors (me/humanize (explainer [m]))})
           ;; FIXME: pprint fails sometime
           ;; (pprint/pprint {;; :entity-map (map #(into {} %) m)
           ;;                 :errors (me/humanize (m/explain db-schema [m]))})
-          )
-        false)
-      true)))
+              )
+            false)
+          true)))))
 
 (defn group-errors-by-entity
   "Groups malli errors by entities. db is used for providing more debugging info"
@@ -109,12 +113,11 @@
   [db]
   (let [datoms (d/datoms db :eavt)
         ent-maps* (db-malli-schema/datoms->entities datoms)
-        schema (update-schema db-malli-schema/DB db {:closed-schema? true})
         ent-maps (mapv
                   ;; Remove some UI interactions adding this e.g. import
                   #(dissoc % :block.temp/fully-loaded?)
                   (db-malli-schema/update-properties-in-ents db ent-maps*))
-        errors (->> ent-maps (m/explain schema) :errors)]
+        errors (-> ent-maps closed-db-schema-explainer :errors)]
     (cond-> {:datom-count (count datoms)
              :entities ent-maps}
       (some? errors)