Ver Fonte

refactor: batch tx

outliner-tx/transact! now operates outliner op one by one instead of
batching processing, the benefit is that we can safely rely on the ops
order, it simplifies code a lot too.
Tienson Qin há 1 ano atrás
pai
commit
0085accf70

+ 14 - 3
deps/db/src/logseq/db.cljs

@@ -9,7 +9,8 @@
             [clojure.set :as set]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.entity-plus]
-            [logseq.db.sqlite.util :as sqlite-util]))
+            [logseq.db.sqlite.util :as sqlite-util]
+            [logseq.db.frontend.delete-blocks :as delete-blocks]))
 
 ;; Use it as an input argument for datalog queries
 (def block-attrs
@@ -54,8 +55,18 @@
   ([repo-or-conn tx-data]
    (transact! repo-or-conn tx-data nil))
   ([repo-or-conn tx-data tx-meta]
-   (let [tx-data (->> (common-util/fast-remove-nils tx-data)
-                      (remove empty?))]
+   (let [tx-data (map (fn [m]
+                        (if (map? m)
+                          (dissoc m :block/children :block/meta :block/top? :block/bottom? :block/anchor
+                                  :block/title :block/body :block/level :block/container :db/other-tx
+                                  :block/unordered)
+                          m)) tx-data)
+         tx-data (->> (common-util/fast-remove-nils tx-data)
+                      (remove empty?))
+         delete-blocks-tx (when-not (string? repo-or-conn)
+                            (delete-blocks/update-refs-and-macros @repo-or-conn tx-data))
+         tx-data (concat tx-data delete-blocks-tx)]
+
      ;; Ensure worker can handle the request sequentially (one by one)
      ;; Because UI assumes that the in-memory db has all the data except the last one transaction
      (when (seq tx-data)

+ 44 - 0
deps/db/src/logseq/db/frontend/delete_blocks.cljs

@@ -0,0 +1,44 @@
+(ns logseq.db.frontend.delete-blocks
+  "Delete refs/macros when deleting blocks"
+  (:require [logseq.common.util :as common-util]
+            [logseq.common.util.block-ref :as block-ref]
+            [logseq.graph-parser.property :as gp-property]
+            [datascript.core :as d]
+            [clojure.string :as string]))
+
+(defn update-refs-and-macros
+  "When a block is deleted, refs are updated and macros associated with the block are deleted"
+  [db txs]
+  (let [retracted-block-ids (->> (keep (fn [tx]
+                                         (when (and (vector? tx)
+                                                    (contains? [:db.fn/retractEntity :db/retractEntity] (first tx)))
+                                           (second tx))) txs))]
+    (when (seq retracted-block-ids)
+      (let [retracted-blocks (map #(d/entity db %) retracted-block-ids)
+            retracted-tx (->> (for [block retracted-blocks]
+                                (let [refs (:block/_refs block)]
+                                  (map (fn [ref]
+                                         (let [id (:db/id ref)
+                                               block-content (gp-property/remove-properties
+                                                              (:block/format block) (:block/content block))
+                                               new-content (some-> (:block/content ref)
+                                                                   (string/replace (re-pattern (common-util/format "(?i){{embed \\(\\(%s\\)\\)\\s?}}" (str (:block/uuid block))))
+                                                                                   block-content)
+                                                                   (string/replace (block-ref/->block-ref (str (:block/uuid block)))
+                                                                                   block-content))
+                                               tx (cond->
+                                                   [[:db/retract (:db/id ref) :block/refs (:db/id block)]
+                                                    [:db/retract (:db/id ref) :block/path-refs (:db/id block)]]
+                                                    new-content
+                                                    (conj [:db/add id :block/content new-content]))]
+                                           {:tx tx})) refs)))
+                              (apply concat))
+            retracted-tx' (mapcat :tx retracted-tx)
+            macros-tx (mapcat (fn [b]
+                              ;; Only delete if last reference
+                                (keep #(when (<= (count (:block/_macros (d/entity db (:db/id %))))
+                                                 1)
+                                         (when (:db/id %) (vector :db.fn/retractEntity (:db/id %))))
+                                      (:block/macros b)))
+                              retracted-blocks)]
+        (concat txs retracted-tx' macros-tx)))))

+ 1 - 1
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -288,7 +288,7 @@
    [:schema/version :int]
    [:graph/uuid :string]
    [:graph/local-tx :string]
-   [:editor/tx-batch-mode? :boolean]])
+   [:editor/counter :int]])
 
 (def db-ident-key-val
   "A key-val map consists of a :db/ident and a specific key val"

+ 3 - 1
deps/db/src/logseq/db/sqlite/common_db.cljs

@@ -161,7 +161,9 @@
                          (when id
                            [{:db/id (:db/id e)
                              :db/ident :logseq.kv/graph-uuid
-                             :graph/uuid id}])))
+                             :graph/uuid id}
+                            {:db/ident :logseq.kv/tx-batch-counter
+                             :editor/counter 0}])))
         favorites (get-favorites db)
         latest-journals (get-latest-journals db 3)
         all-files (get-all-files db)

+ 11 - 29
deps/outliner/src/logseq/outliner/core.cljs

@@ -23,16 +23,6 @@
             [logseq.db.sqlite.create-graph :as sqlite-create-graph]
             [frontend.worker.batch-tx :include-macros true :as batch-tx]))
 
-(def ^:private ^:dynamic *transaction-data*
-  "Stores transaction-data that are generated by one or more write-operations,
-  see also `logseq.outliner.transaction/transact!`"
-  nil)
-
-(def ^:private ^:dynamic #_:clj-kondo/ignore *transaction-opts*
-  "Stores transaction opts that are generated by one or more write-operations,
-  see also `logseq.outliner.transaction/transact!`"
-  nil)
-
 
 (def ^:private block-map
   (mu/optional-keys
@@ -463,17 +453,11 @@
     (assert (ds/outliner-txs-state? txs-state)
             "db should be satisfied outliner-tx-state?")
     (let [block-id (otree/-get-id this conn)
-          children-moved? (and (= (count *transaction-data*) 1)
-                               (let [{:keys [move-op move-blocks]} (:tx-meta (nth *transaction-data* 0))]
-                                 (and (= move-op :move-blocks)
-                                      (every? (fn [id] (= block-id (:block/uuid (:block/parent (d/entity @conn id))))) move-blocks))))
-          ids (if children-moved?
-                [block-id]
-                (->>
-                 (let [children (ldb/get-block-children @conn block-id)
-                       children-ids (map :block/uuid children)]
-                   (conj children-ids block-id))
-                 (remove nil?)))
+          ids (->>
+               (let [children (ldb/get-block-children @conn block-id)
+                     children-ids (map :block/uuid children)]
+                 (conj children-ids block-id))
+               (remove nil?))
           txs (map (fn [id] [:db.fn/retractEntity [:block/uuid id]]) ids)
           page-tx (let [block (d/entity @conn [:block/uuid block-id])]
                     (when (:block/pre-block? block)
@@ -1072,7 +1056,7 @@
                          (set))
             move-parents-to-child? (some parents (map :db/id blocks))]
         (when-not move-parents-to-child?
-          (batch-tx/with-batch-tx-mode conn
+          (batch-tx/with-batch-tx-mode conn {:outliner-op :move-blocks}
             (doseq [[idx block] (map vector (range (count blocks)) blocks)]
               (let [first-block? (zero? idx)
                     sibling? (if first-block? sibling? true)
@@ -1180,10 +1164,9 @@
                         right-siblings (->> (get-right-siblings conn (block db last-top-block))
                                             (map :data))]
                     (if (seq right-siblings)
-                      (let [result2 (if-let [last-direct-child-id (ldb/get-block-last-direct-child-id db (:db/id last-top-block))]
-                                      (move-blocks repo conn right-siblings (d/entity db last-direct-child-id) (merge opts {:sibling? true}))
-                                      (move-blocks repo conn right-siblings last-top-block (merge opts {:sibling? false})))]
-                        (concat-tx-fn result result2))
+                      (if-let [last-direct-child-id (ldb/get-block-last-direct-child-id db (:db/id last-top-block))]
+                        (move-blocks repo conn right-siblings (d/entity db last-direct-child-id) (merge opts {:sibling? true}))
+                        (move-blocks repo conn right-siblings last-top-block (merge opts {:sibling? false})))
                       result)))))))))))
 
 ;;; ### write-operations have side-effects (do transactions) ;;;;;;;;;;;;;;;;
@@ -1221,11 +1204,10 @@
 (defn- op-transact!
   [fn-var & args]
   {:pre [(var? fn-var)]}
-  (when (nil? *transaction-data*)
-    (throw (js/Error. (str (:name (meta fn-var)) " is not used in (transact! ...)"))))
   (let [result (apply @fn-var args)]
     (validate-tx-data @(nth args 1) (:tx-data result) (:tx-meta result) args)
-    (conj! *transaction-data* (select-keys result [:tx-data :tx-meta]))
+    (when result
+      (ldb/transact! (second args) (:tx-data result) (:tx-meta result)))
     result))
 
 (defn save-block!

+ 1 - 99
deps/outliner/src/logseq/outliner/datascript.cljs

@@ -1,12 +1,5 @@
 (ns logseq.outliner.datascript
-  "Provides fns related to wrapping datascript's transact!"
-  (:require [logseq.common.util :as common-util]
-            [logseq.common.util.block-ref :as block-ref]
-            [logseq.graph-parser.property :as gp-property]
-            [datascript.core :as d]
-            [clojure.string :as string]
-            [logseq.db :as ldb]
-            [promesa.core :as p]))
+  "Provides fns related to batch txs state")
 
 (defn new-outliner-txs-state [] (atom []))
 
@@ -15,94 +8,3 @@
   (and
    (instance? cljs.core/Atom state)
    (coll? @state)))
-
-(defn- remove-nil-from-transaction
-  [txs]
-  (some->> (common-util/remove-nils txs)
-           (map (fn [x]
-                  (if (map? x)
-                    (update-vals x (fn [v]
-                                     (if (vector? v)
-                                       (remove nil? v)
-                                       v)))
-                    x)))))
-
-(defn- update-refs-and-macros
-  "When a block is deleted, refs are updated and macros associated with the block are deleted"
-  [txs db repo opts set-state-fn]
-  (if (= :delete-blocks (:outliner-op opts))
-    (let [retracted-block-ids (->> (keep (fn [tx]
-                                           (when (and (vector? tx)
-                                                      (= :db.fn/retractEntity (first tx)))
-                                             (second tx))) txs))
-          retracted-blocks (map #(d/entity db %) retracted-block-ids)
-          retracted-tx (->> (for [block retracted-blocks]
-                              (let [refs (:block/_refs block)]
-                                (map (fn [ref]
-                                       (let [id (:db/id ref)
-                                             block-content (gp-property/remove-properties
-                                                            (:block/format block) (:block/content block))
-                                             new-content (some-> (:block/content ref)
-                                                                 (string/replace (re-pattern (common-util/format "(?i){{embed \\(\\(%s\\)\\)\\s?}}" (str (:block/uuid block))))
-                                                                                 block-content)
-                                                                 (string/replace (block-ref/->block-ref (str (:block/uuid block)))
-                                                                                 block-content))
-                                             tx (cond->
-                                                 [[:db/retract (:db/id ref) :block/refs (:db/id block)]
-                                                  [:db/retract (:db/id ref) :block/path-refs (:db/id block)]]
-                                                  new-content
-                                                  (conj [:db/add id :block/content new-content]))
-                                             revert-tx (cond->
-                                                        [[:db/add (:db/id ref) :block/refs (:db/id block)]
-                                                         [:db/add (:db/id ref) :block/path-refs (:db/id block)]]
-                                                         (:block/content ref)
-                                                         (conj [:db/add id :block/content (:block/content ref)]))]
-                                         {:tx tx :revert-tx revert-tx})) refs)))
-                            (apply concat))
-          retracted-tx' (mapcat :tx retracted-tx)
-          revert-tx (mapcat :revert-tx retracted-tx)
-          macros-tx (mapcat (fn [b]
-                              ;; Only delete if last reference
-                              (keep #(when (<= (count (:block/_macros (d/entity db (:db/id %))))
-                                               1)
-                                       (when (:db/id %) (vector :db.fn/retractEntity (:db/id %))))
-                                    (:block/macros b)))
-                            retracted-blocks)]
-      (when (and (seq retracted-tx') (fn? set-state-fn))
-        (set-state-fn [:editor/last-replace-ref-content-tx repo]
-                      {:retracted-block-ids retracted-block-ids
-                       :revert-tx revert-tx}))
-      (concat txs retracted-tx' macros-tx))
-    txs))
-
-(defn transact!
-  [txs tx-meta {:keys [repo conn set-state-fn]}]
-  (let [txs (map (fn [m]
-                   (if (map? m)
-                     (dissoc m :block/children :block/meta :block/top? :block/bottom? :block/anchor
-                             :block/title :block/body :block/level :block/container :db/other-tx
-                             :block/unordered)
-                     m)) txs)
-        txs (remove-nil-from-transaction txs)
-        txs (cond-> txs
-              (= :delete-blocks (:outliner-op tx-meta))
-              (update-refs-and-macros @conn repo tx-meta set-state-fn)
-
-              true
-              (distinct))]
-
-    (when (seq txs)
-
-      ;; (prn :debug "DB transact")
-      ;; (cljs.pprint/pprint txs)
-
-      (try
-        (when-not (exists? js/process)  ; UI thread
-          (.time js/console "DB transact"))
-        (let [result (ldb/transact! conn txs (assoc tx-meta :outliner/transact? true))]
-          (when-not (exists? js/process)
-            (p/then result (fn [] (.timeEnd js/console "DB transact"))))
-          result)
-        (catch :default e
-          (js/console.error e)
-          (throw e))))))

+ 1 - 1
deps/outliner/src/logseq/outliner/op.cljs

@@ -46,7 +46,7 @@
   [repo conn ops date-formatter opts]
   (assert (ops-validator ops) ops)
   (let [opts' (assoc opts
-                     :transact-opts {:repo repo :conn conn}
+                     :transact-opts {:conn conn}
                      :local-tx? true)
         *insert-result (atom nil)]
     (outliner-tx/transact!

+ 7 - 33
deps/outliner/src/logseq/outliner/transaction.cljc

@@ -16,46 +16,20 @@
   If no transactions are included in `body`, it does not save a transaction.
   `Args`:
     `opts`: Every key is optional, opts except `additional-tx` will be transacted as `tx-meta`.
-            {:outliner-op \"For example, :save-block, :insert-blocks, etc. \"
+            {:transact-opts {:conn conn} \"Datascript conn\"
              :additional-tx \"Additional tx data that can be bundled together
                               with the body in this macro.\"
              :persist-op? \"Boolean, store ops into db (sqlite), by default,
                             its value depends on (config/db-based-graph? repo)\"}
   `Example`:
-  (transact! {:graph \"test\"}
+  (transact! {:conn db-conn}
     (insert-blocks! ...)
     ;; do something
     (move-blocks! ...)
     (delete-blocks! ...))"
   [opts & body]
-  `(let [transact-data# logseq.outliner.core/*transaction-data*
-         transaction-opts# logseq.outliner.core/*transaction-opts*
-         opts*# ~opts
-         _# (assert (or (map? opts*#) (symbol? opts*#)) (str "opts is not a map or symbol, type: " (type opts*#)))
-         opts# (if transact-data#
-                 (assoc opts*# :nested-transaction? true)
-                 opts*#)]
-     (if transact-data#
-       (do
-         (when transaction-opts#
-           (conj! transaction-opts# opts#))
-         ~@body)
-       (let [transaction-args# (cond-> {}
-                                 (get opts*# :persist-op? true)
-                                 (assoc :persist-op? true))]
-         (binding [logseq.outliner.core/*transaction-data* (transient [])
-                   logseq.outliner.core/*transaction-opts* (transient [])]
-           (conj! logseq.outliner.core/*transaction-opts* opts#)
-           ~@body
-           (let [r# (persistent! logseq.outliner.core/*transaction-data*)
-                 tx# (mapcat :tx-data r#)
-                 ;; FIXME: should we merge all the tx-meta?
-                 tx-meta# (first (map :tx-meta r#))
-                 all-tx# (concat tx# (:additional-tx opts#))
-                 o# (persistent! logseq.outliner.core/*transaction-opts*)
-                 full-opts# (apply merge (reverse o#))
-                 opts## (merge (dissoc full-opts# :additional-tx :current-block :nested-transaction?) tx-meta#)]
-
-             (when (seq all-tx#) ;; If it's empty, do nothing
-               (when-not (:nested-transaction? opts#) ; transact only for the whole transaction
-                 (logseq.outliner.datascript/transact! all-tx# (dissoc opts## :transact-opts) (:transact-opts opts##))))))))))
+  `(let [opts# (dissoc ~opts :transact-opts :current-block)]
+     (frontend.worker.batch-tx/with-batch-tx-mode
+      (:conn (:transact-opts ~opts))
+      opts#
+      ~@body)))

+ 14 - 10
src/main/frontend/worker/batch_tx.clj

@@ -1,17 +1,21 @@
 (ns frontend.worker.batch-tx
-  "Macro for batch-tx fns"
-  (:require [datascript.core :as d]))
+  "Macro for batch-tx fns")
 
 (defmacro with-batch-tx-mode
   "1. start batch-tx mode
   2. run body
   3. exit batch-tx mode"
-  [conn & body]
-  `(do
-     (d/transact! ~conn [{:db/ident :logseq.kv/tx-batch-mode? :editor/tx-batch-mode? true}]
-                  {:gen-undo-ops? false})
-     (frontend.worker.batch-tx/set-batch-db-before! @~conn)
+  [conn {:keys [additional-tx] :as opts} & body]
+  `(let [tx-batch-counter# (get (d/entity @~conn :logseq.kv/tx-batch-counter) :editor/counter 0)
+         outside-batch?# (zero? tx-batch-counter#)
+         tx-meta# (dissoc ~opts :additional-tx :transact-opts)]
+     (logseq.db/transact! ~conn
+                          [{:db/ident :logseq.kv/tx-batch-counter :editor/counter (inc tx-batch-counter#)}]
+                          {:batch-tx-begin? true})
+     (when outside-batch?# (frontend.worker.batch-tx/set-batch-db-before! @~conn))
      ~@body
-     (d/transact! ~conn [{:db/ident :logseq.kv/tx-batch-mode? :editor/tx-batch-mode? false}]
-                  {:gen-undo-ops? false})
-     (frontend.worker.batch-tx/exit-batch-txs-mode!)))
+     (when (seq ~additional-tx)
+       (logseq.db/transact! ~conn ~additional-tx {}))
+     (logseq.db/transact! ~conn [{:db/ident :logseq.kv/tx-batch-counter :editor/counter tx-batch-counter#}]
+       (assoc tx-meta# :batch-tx-end? true))
+     (when outside-batch?# (frontend.worker.batch-tx/exit-batch-txs-mode!))))

+ 50 - 35
src/main/frontend/worker/db_listener.cljs

@@ -6,7 +6,8 @@
             [frontend.worker.search :as search]
             [frontend.worker.state :as worker-state]
             [frontend.worker.util :as worker-util]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [frontend.worker.batch-tx :as batch-tx]))
 
 
 (defn- entity-datoms=>attr->datom
@@ -35,28 +36,27 @@
 
 (defmethod listen-db-changes :sync-db-to-main-thread
   [_ {:keys [tx-meta repo conn] :as tx-report}]
-  (let [{:keys [pipeline-replace? from-disk?]} tx-meta]
-    (when-not pipeline-replace?
-      (let [result (worker-pipeline/invoke-hooks repo conn tx-report (worker-state/get-context))
-            tx-report' (:tx-report result)]
-        (when result
-          (let [data (merge
-                      {:request-id (:request-id tx-meta)
-                       :repo repo
-                       :tx-data (:tx-data tx-report')
-                       :tx-meta tx-meta}
-                      (dissoc result :tx-report))]
-            (worker-util/post-message :sync-db-changes data))
+  (let [{:keys [from-disk?]} tx-meta
+        result (worker-pipeline/invoke-hooks repo conn tx-report (worker-state/get-context))
+        tx-report' (:tx-report result)]
+    (when result
+      (let [data (merge
+                  {:request-id (:request-id tx-meta)
+                   :repo repo
+                   :tx-data (:tx-data tx-report')
+                   :tx-meta tx-meta}
+                  (dissoc result :tx-report))]
+        (worker-util/post-message :sync-db-changes data))
 
-          (when-not from-disk?
-            (p/do!
-             (let [{:keys [blocks-to-remove-set blocks-to-add]} (search/sync-search-indice repo tx-report')
-                   ^js wo (worker-state/get-worker-object)]
-               (when wo
-                 (when (seq blocks-to-remove-set)
-                   (.search-delete-blocks wo repo (bean/->js blocks-to-remove-set)))
-                 (when (seq blocks-to-add)
-                   (.search-upsert-blocks wo repo (bean/->js blocks-to-add))))))))))))
+      (when-not from-disk?
+        (p/do!
+         (let [{:keys [blocks-to-remove-set blocks-to-add]} (search/sync-search-indice repo tx-report')
+               ^js wo (worker-state/get-worker-object)]
+           (when wo
+             (when (seq blocks-to-remove-set)
+               (.search-delete-blocks wo repo (bean/->js blocks-to-remove-set)))
+             (when (seq blocks-to-add)
+               (.search-upsert-blocks wo repo (bean/->js blocks-to-add))))))))))
 
 
 (defn listen-db-changes!
@@ -67,16 +67,31 @@
     (prn :listen-db-changes! (keys handlers))
     (d/unlisten! conn ::listen-db-changes!)
     (d/listen! conn ::listen-db-changes!
-               (fn [{:keys [tx-data] :as args}]
-                 (let [datom-vec-coll (map vec tx-data)
-                       id->same-entity-datoms (group-by first datom-vec-coll)
-                       id-order (distinct (map first datom-vec-coll))
-                       same-entity-datoms-coll (map id->same-entity-datoms id-order)
-                       id->attr->datom (update-vals id->same-entity-datoms entity-datoms=>attr->datom)
-                       args* (assoc args
-                                    :repo repo
-                                    :conn conn
-                                    :id->attr->datom id->attr->datom
-                                    :same-entity-datoms-coll same-entity-datoms-coll)]
-                   (doseq [[k handler-fn] handlers]
-                     (handler-fn k args*)))))))
+               (fn [{:keys [tx-data db-before db-after tx-meta] :as tx-report}]
+                 (let [pipeline-replace? (:pipeline-replace? tx-meta)
+                       batch-processing? (> (:editor/counter (d/entity db-after :logseq.kv/tx-batch-counter)) 0)]
+                   (when-not pipeline-replace?
+                     (if batch-processing?
+                       (batch-tx/conj-batch-txs! tx-data)
+                       (let [exiting-batch-mode? (> (:editor/counter (d/entity db-before :logseq.kv/tx-batch-counter)) 0)
+                             db-before (if exiting-batch-mode?
+                                         (batch-tx/get-batch-db-before)
+                                         (:db-before tx-report))
+                             tx-data (if exiting-batch-mode?
+                                       (batch-tx/get-batch-txs)
+                                       (concat (:tx-data tx-report) tx-data))
+                             tx-report (assoc tx-report
+                                              :db-before db-before
+                                              :tx-data tx-data)
+                             datom-vec-coll (map vec tx-data)
+                             id->same-entity-datoms (group-by first datom-vec-coll)
+                             id-order (distinct (map first datom-vec-coll))
+                             same-entity-datoms-coll (map id->same-entity-datoms id-order)
+                             id->attr->datom (update-vals id->same-entity-datoms entity-datoms=>attr->datom)
+                             args* (assoc tx-report
+                                          :repo repo
+                                          :conn conn
+                                          :id->attr->datom id->attr->datom
+                                          :same-entity-datoms-coll same-entity-datoms-coll)]
+                         (doseq [[k handler-fn] handlers]
+                           (handler-fn k args*))))))))))

+ 8 - 26
src/main/frontend/worker/pipeline.cljs

@@ -1,7 +1,6 @@
 (ns frontend.worker.pipeline
   "Pipeline work after transaction"
   (:require [datascript.core :as d]
-            [frontend.worker.batch-tx :as batch-tx]
             [frontend.worker.db.fix :as db-fix]
             [frontend.worker.file :as file]
             [frontend.worker.react :as worker-react]
@@ -75,31 +74,15 @@
     (fix-db! conn tx-report context)))
 
 (defn invoke-hooks
-  [repo conn {:keys [db-before db-after] :as tx-report} context]
+  [repo conn tx-report context]
   (let [tx-meta (:tx-meta tx-report)
-        {:keys [from-disk? new-graph?]} tx-meta
-        now-batch-processing? (:editor/tx-batch-mode? (d/entity db-after :logseq.kv/tx-batch-mode?))]
+        {:keys [from-disk? new-graph?]} tx-meta]
     (cond
-      now-batch-processing?
-      (do
-        (batch-tx/conj-batch-txs! (:tx-data tx-report))
-        nil)
-
       (or from-disk? new-graph?)
       {:tx-report tx-report}
 
       :else
-      (let [exiting-batch-mode? (:editor/tx-batch-mode? (d/entity db-before :logseq.kv/tx-batch-mode?))
-            db-before (if exiting-batch-mode?
-                        (batch-tx/get-batch-db-before)
-                        (:db-before tx-report))
-            tx-data (if exiting-batch-mode?
-                      (batch-tx/get-batch-txs)
-                      (:tx-data tx-report))
-            tx-report (assoc tx-report
-                             :db-before db-before
-                             :tx-data tx-data)
-            {:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)
+      (let [{:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)
             _ (when (sqlite-util/local-file-based-graph? repo)
                 (let [page-ids (distinct (map :db/id pages))]
                   (doseq [page-id page-ids]
@@ -130,14 +113,13 @@
                           (ldb/transact! conn replace-tx {:replace? true
                                                           :pipeline-replace? true}))
                         (do
-                          (d/store @conn)
+                          (when-not (exists? js/process)
+                            (d/store @conn))
                           tx-report))
             fix-tx-data (validate-and-fix-db! repo conn tx-report context)
-            full-tx-data (if exiting-batch-mode?
-                           (:tx-data tx-report)
-                           (concat (:tx-data tx-report)
-                                   fix-tx-data
-                                   (:tx-data tx-report')))
+            full-tx-data (concat (:tx-data tx-report)
+                                 fix-tx-data
+                                 (:tx-data tx-report'))
             final-tx-report (assoc tx-report' :tx-data full-tx-data)
             affected-query-keys (when-not (:importing? context)
                                   (worker-react/get-affected-queries-keys final-tx-report))]

+ 1 - 1
src/main/frontend/worker/rtc/core.cljs

@@ -642,7 +642,7 @@
               update-page-ops (vals update-page-ops-map)
               remove-page-ops (vals remove-page-ops-map)]
 
-          (batch-tx/with-batch-tx-mode conn
+          (batch-tx/with-batch-tx-mode conn {:rtc-tx? true}
             (js/console.groupCollapsed "rtc/apply-remote-ops-log")
             (worker-util/profile :apply-remote-update-page-ops (apply-remote-update-page-ops repo conn date-formatter update-page-ops))
             (worker-util/profile :apply-remote-remove-ops (apply-remote-remove-ops repo conn date-formatter remove-ops))

+ 4 - 3
src/main/frontend/worker/undo_redo.cljs

@@ -385,7 +385,7 @@ when undo this op, this original entity-map will be transacted back into db")
   [repo page-block-uuid conn]
   (if-let [ops (not-empty (pop-undo-ops repo page-block-uuid))]
     (let [redo-ops-to-push (transient [])]
-      (batch-tx/with-batch-tx-mode conn
+      (batch-tx/with-batch-tx-mode conn {:undo? true}
         (doseq [op ops]
           (let [rev-ops (reverse-op @conn op)
                 r (reverse-apply-op op conn repo)]
@@ -404,7 +404,7 @@ when undo this op, this original entity-map will be transacted back into db")
   [repo page-block-uuid conn]
   (if-let [ops (not-empty (pop-redo-ops repo page-block-uuid))]
     (let [undo-ops-to-push (transient [])]
-      (batch-tx/with-batch-tx-mode conn
+      (batch-tx/with-batch-tx-mode conn {:redo? true}
         (doseq [op ops]
           (let [rev-ops (reverse-op @conn op)
                 r (reverse-apply-op op conn repo)]
@@ -510,7 +510,8 @@ when undo this op, this original entity-map will be transacted back into db")
 (defmethod db-listener/listen-db-changes :gen-undo-ops
   [_ {:keys [_tx-data tx-meta db-before db-after
              repo id->attr->datom same-entity-datoms-coll]}]
-  (when (:gen-undo-ops? tx-meta true)
+  (when (and (:gen-undo-ops? tx-meta true)
+             (not (or (:undo? tx-meta) (:redo? tx-meta))))
     (generate-undo-ops repo db-before db-after same-entity-datoms-coll id->attr->datom
                        (:gen-undo-boundary-op? tx-meta true))))