From 39ea6207bd68fb84b5c3ddd80491b5c33b40ed82 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 31 Mar 2026 17:19:01 +0800 Subject: [PATCH] fix: debounced store shouldn't be used for batch-transact! --- deps/db/src/logseq/db.cljs | 20 ++++---- deps/db/src/logseq/db/common/normalize.cljs | 31 +++++++++++ .../src/logseq/db/frontend/malli_schema.cljs | 8 +-- deps/db/src/logseq/db/frontend/validate.cljs | 2 +- src/main/frontend/worker/db/validate.cljs | 2 +- src/main/frontend/worker/sync/apply_txs.cljs | 51 +++++++------------ src/test/frontend/worker/db_sync_test.cljs | 2 +- 7 files changed, 66 insertions(+), 50 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 1fa33dccaa..a033a660b5 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -99,8 +99,9 @@ (defn debounced-store-db [conn] (when-some [_storage (storage/storage @conn)] - (let [f (or @*debounce-fn d/store)] - (f @conn)))) + (when-not (:batch-tx? @conn) + (let [f (or @*debounce-fn d/store)] + (f @conn))))) (defn- transact-sync [conn tx-data tx-meta] @@ -112,12 +113,13 @@ (or (:rtc-download-graph? tx-meta) (:reset-conn! tx-meta) (:initial-db? tx-meta) - (:skip-validate-db? db) (:skip-validate-db? tx-meta false) + ;; used by `batch-transact-with-temp-conn!` + (:skip-validate-db? @conn) (:logseq.graph-parser.exporter/new-graph? tx-meta)))) (let [tx-report* (d/with db tx-data tx-meta) pipeline-f @*transact-pipeline-fn - tx-report (if-let [f pipeline-f] (f tx-report*) tx-report*) + tx-report (if pipeline-f (pipeline-f tx-report*) tx-report*) _ (throw-if-page-has-block-parent! (:db-after tx-report) (:tx-data tx-report)) [validate-result errors] (db-validate/validate-tx-report tx-report nil)] (cond @@ -126,11 +128,8 @@ (seq (:tx-data tx-report))) ;; perf enhancement: avoid repeated call on `d/with` (reset! conn (:db-after tx-report)) - (if (:batch-tx? @conn) - (dc/run-callbacks conn tx-report) - (do - (debounced-store-db conn) - (dc/run-callbacks conn tx-report)))) + (debounced-store-db conn) + (dc/run-callbacks conn tx-report)) :else (do @@ -237,7 +236,8 @@ (swap! conn dissoc :skip-store? :batch-tx?) - (debounced-store-db conn) + (when-some [_storage (storage/storage @conn)] + (d/store @conn)) (let [batch-tx-data @*tx-data _ (reset! *tx-data nil) diff --git a/deps/db/src/logseq/db/common/normalize.cljs b/deps/db/src/logseq/db/common/normalize.cljs index c93f2eaeb3..a7e60a9e1e 100644 --- a/deps/db/src/logseq/db/common/normalize.cljs +++ b/deps/db/src/logseq/db/common/normalize.cljs @@ -101,6 +101,36 @@ ;; sort by :tx, use nth to make this fn works on both vector and datom (sort-by #(nth % 3)))) +(defn- retract-entity-op? + [item] + (and (= 2 (count item)) + (= :db/retractEntity (first item)))) + +(defn- retract-entity-match-keys + [e] + (if (and (vector? e) (= :block/uuid (first e))) + (let [uuid (second e)] + #{e uuid (str uuid)}) + #{e})) + +(defn- reorder-retract-entity-first + [tx-data] + (let [retract-ops (filter retract-entity-op? tx-data) + retract-keys (->> retract-ops + (map second) + (mapcat retract-entity-match-keys) + set) + datom-for-retracted-eid? + (fn [item] + (and (= 5 (count item)) + (contains? retract-keys (second item)))) + datoms-for-retracted-eids (filter datom-for-retracted-eid? tx-data) + others (remove (fn [item] + (or (retract-entity-op? item) + (datom-for-retracted-eid? item))) + tx-data)] + (concat retract-ops datoms-for-retracted-eids others))) + (defn normalize-tx-data [db-after db-before tx-data] (let [title-updated-entities @@ -150,4 +180,5 @@ e)] [op e']))))) (remove-retract-entity-ref db-after) + reorder-retract-entity-first distinct))) diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 250daf3d36..adecac1943 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -290,7 +290,8 @@ [:block/refs {:optional true} [:set :int]] [:block/tx-id {:optional true} :int] [:block/collapsed? {:optional true} :boolean] - [:block/warning {:optional true} [:keyword]]]) + [:block/warning {:optional true} [:keyword]] + [:logseq.property/created-by-ref {:optional true} :int]]) (def page-attrs "Common attributes for pages" @@ -441,13 +442,13 @@ [:block/uuid :uuid] [:logseq.property.reaction/emoji-id :string] [:logseq.property.reaction/target :int] - [:block/properties {:optional true} block-properties] [:block/created-at :int] [:block/tx-id {:optional true} :int] + [:logseq.property/created-by-ref {:optional true} :int] [:block/refs {:optional true} [:set :int]]])) (def property-history-block* - [:map + [:map {:error/path ["property-history-block"]} [:block/uuid :uuid] [:block/created-at :int] [:block/updated-at {:optional true} :int] @@ -455,6 +456,7 @@ [:logseq.property.history/property :int] [:logseq.property.history/ref-value {:optional true} :int] [:logseq.property.history/scalar-value {:optional true} :any] + [:block/properties {:optional true} block-properties] [:block/tx-id {:optional true} :int]]) (def property-history-block diff --git a/deps/db/src/logseq/db/frontend/validate.cljs b/deps/db/src/logseq/db/frontend/validate.cljs index 9c5208d1b3..48fbefd421 100644 --- a/deps/db/src/logseq/db/frontend/validate.cljs +++ b/deps/db/src/logseq/db/frontend/validate.cljs @@ -82,7 +82,7 @@ :dispatch-key (->> (dissoc ent :db/id) (db-malli-schema/entity-dispatch-key db)) :errors errors'}))))) -(defn validate-db! +(defn validate-db "Validates all the entities of the given db using :eavt datoms. Returns a map with info about db being validated. If there are errors, they are placed on :errors and grouped by entity" diff --git a/src/main/frontend/worker/db/validate.cljs b/src/main/frontend/worker/db/validate.cljs index 3d80553867..6294a54041 100644 --- a/src/main/frontend/worker/db/validate.cljs +++ b/src/main/frontend/worker/db/validate.cljs @@ -231,7 +231,7 @@ (fix-num-prefix-db-idents! conn) (let [db @conn - {:keys [errors datom-count entities]} (db-validate/validate-db! db) + {:keys [errors datom-count entities]} (db-validate/validate-db db) invalid-entity-ids (distinct (map (fn [e] (:db/id (:entity e))) errors))] (doseq [error errors] diff --git a/src/main/frontend/worker/sync/apply_txs.cljs b/src/main/frontend/worker/sync/apply_txs.cljs index 2f7515459b..2a488ad66b 100644 --- a/src/main/frontend/worker/sync/apply_txs.cljs +++ b/src/main/frontend/worker/sync/apply_txs.cljs @@ -206,6 +206,10 @@ {:keys [forward-outliner-ops inverse-outliner-ops]} (derive-history-outliner-ops db-before db-after tx-data tx-meta) inferred-outliner-ops?' (inferred-outliner-ops? tx-meta)] + ;; (prn :debug :forward-outliner-ops) + ;; (cljs.pprint/pprint forward-outliner-ops) + ;; (prn :debug :inverse-outliner-ops) + ;; (cljs.pprint/pprint inverse-outliner-ops) (ldb/transact! conn [{:db-sync/tx-id tx-id :db-sync/normalized-tx-data normalized-tx-data :db-sync/reversed-tx-data reversed-datoms @@ -469,38 +473,17 @@ (p/catch (fn [error] (js/console.error error)))))))))))))) -(defn- remote-tx-debug-meta - [temp-tx-meta remote-txs index {:keys [t outliner-op]}] - (cond-> (assoc temp-tx-meta - :op :transact-remote-tx-data - :skip-validate-db? true - :remote-tx-index (inc index) - :remote-tx-count (count remote-txs)) - (number? t) (assoc :remote-t t) - outliner-op (assoc :outliner-op outliner-op))) - -(defn- local-tx-debug-meta - [tx-meta local-txs index local-tx op] - (cond-> (assoc tx-meta - :op op - :local-tx-index (inc index) - :local-tx-count (count local-txs)) - (:tx-id local-tx) (assoc :local-tx-id (:tx-id local-tx)) - (:outliner-op local-tx) (assoc :outliner-op (:outliner-op local-tx)))) - (defn- reverse-history-action! - [conn local-txs index local-tx temp-tx-meta] + [conn local-tx] (if-let [tx-data (seq (:reversed-tx local-tx))] - (d/transact! conn - tx-data - (local-tx-debug-meta temp-tx-meta local-txs index local-tx :reverse)) + (ldb/transact! conn tx-data {:reverse? true}) (invalid-rebase-op! :reverse-history-action {:reason :missing-reversed-tx-data :tx-id (:tx-id local-tx) :outliner-op (:outliner-op local-tx)}))) (defn- transact-remote-txs! - [conn remote-txs temp-tx-meta] + [conn remote-txs] (loop [remaining remote-txs index 0 results []] @@ -508,9 +491,7 @@ (let [tx-data (->> (:tx-data remote-tx) seq) report (try - (ldb/transact! conn - tx-data - (remote-tx-debug-meta temp-tx-meta remote-txs index remote-tx)) + (ldb/transact! conn tx-data {:transact-remote? true}) (catch :default e (js/console.error e) (log/error ::transact-remote-txs! {:remote-tx remote-tx @@ -525,7 +506,7 @@ results))) (defn reverse-local-txs! - [conn local-txs temp-tx-meta] + [conn local-txs] ;; (prn :debug :local-txs local-txs) (doall (->> local-txs @@ -533,7 +514,7 @@ (map-indexed (fn [index local-tx] (try - (reverse-history-action! conn local-txs index local-tx temp-tx-meta) + (reverse-history-action! conn local-tx) (catch :default e (log/error ::reverse-local-tx-error {:index index @@ -635,7 +616,7 @@ block-uuid (:block/uuid block) block-ent (when block-uuid (d/entity db [:block/uuid block-uuid])) - block-base (dissoc block :db/id :block/order) + block-base (dissoc block :db/id) block' (merge block-base (op-construct/rewrite-block-title-with-retracted-refs db block-base))] (if (some? block-ent) @@ -912,17 +893,19 @@ (let [tx-meta {:rtc-tx? true :with-local-changes? true} *rebase-tx-reports (atom [])] + ;; (prn :debug :apply-remote-tx (first remote-txs)) (try (ldb/batch-transact! conn tx-meta (fn [conn] - (reverse-local-txs! conn local-txs {:rtc-tx? true}) + (reverse-local-txs! conn local-txs) - (transact-remote-txs! conn remote-txs tx-meta) + (transact-remote-txs! conn remote-txs) (let [rebase-tx-report (rebase-local-txs! repo conn local-txs)] (fix-tx! conn rebase-tx-report {:outliner-op :rebase}))) + {:listen-db (fn [{:keys [tx-meta tx-data] :as tx-report}] (when (and (= :rebase (:outliner-op tx-meta)) (seq tx-data)) @@ -941,13 +924,13 @@ (worker-undo-redo/clear-history! repo))))) (defn- apply-remote-tx-without-local-changes! - [{:keys [conn remote-txs temp-tx-meta]}] + [{:keys [conn remote-txs]}] (ldb/batch-transact-with-temp-conn! conn {:rtc-tx? true :without-local-changes? true} (fn [conn] - (transact-remote-txs! conn remote-txs temp-tx-meta)))) + (transact-remote-txs! conn remote-txs)))) (defn apply-remote-txs! [repo client remote-txs] diff --git a/src/test/frontend/worker/db_sync_test.cljs b/src/test/frontend/worker/db_sync_test.cljs index d2f0d4ca82..bd7c8ddeb7 100644 --- a/src/test/frontend/worker/db_sync_test.cljs +++ b/src/test/frontend/worker/db_sync_test.cljs @@ -1212,7 +1212,7 @@ :reversed-tx [[:db/add child-id :block/title "raw reverse"]]}] (with-datascript-conns conn client-ops-conn (fn [] - (let [reports (#'sync-apply/reverse-local-txs! conn [local-tx] {:rtc-tx? true})] + (let [reports (#'sync-apply/reverse-local-txs! conn [local-tx])] (is (= 1 (count reports))) (is (= "raw reverse" (:block/title (d/entity @conn [:block/uuid child-uuid]))))))))))