fix: parent cycle

This commit is contained in:
Tienson Qin
2026-01-08 04:15:24 +08:00
parent 7fc898df9f
commit 9dd5745506
4 changed files with 53 additions and 38 deletions

View File

@@ -9,10 +9,8 @@
(defn- ref->eid [db ref]
(cond
(nil? ref) nil
(number? ref) ref
(number? ref) (when (pos? ref) ref)
(vector? ref) (d/entid db ref)
(instance? Entity ref) (.-eid ^Entity ref)
(uuid? ref) (d/entid db [:block/uuid ref])
(keyword? ref) (d/entid db [:db/ident ref])
:else nil))
@@ -64,8 +62,6 @@
"Returns a map with cycle details when applying tx-data would introduce a cycle.
Otherwise returns nil."
[db tx-data]
(prn :debug :detect-cycle
tx-data)
(reduce
(fn [_ attr]
(let [updates (attr-updates-from-tx tx-data attr)
@@ -109,6 +105,6 @@
(when parent-uuid
[:block/uuid parent-uuid]))
:else current-raw)]
(assoc acc (pr-str entity-ref) current)))
(assoc acc [:block/uuid (:block/uuid (d/entity db eid))] current)))
{}
updates)))

View File

@@ -106,8 +106,9 @@
(defn- cycle-reject-response [db tx-data {:keys [attr]}]
{:type "tx/reject"
:reason "cycle"
:attr (name attr)
:server_values (common/write-transit (cycle/server-values-for db tx-data attr))})
:data (common/write-transit
{:attr attr
:server_values (cycle/server-values-for db tx-data attr)})})
(defn- journal-page-info []
(let [now (common/now-ms)
@@ -171,9 +172,7 @@
tx-data))
(defn- fix-missing-parent [db tx-data]
(let [_ (prn :debug 1)
updates (attr-updates-from-tx tx-data :block/parent)
_ (prn :debug 2)
(let [updates (attr-updates-from-tx tx-data :block/parent)
journal (journal-page-info)
journal-ref [:block/uuid (:uuid journal)]
journal-tx (build-journal-page-tx db journal)
@@ -301,6 +300,16 @@
[op e' a v'])))
tx-data))
(defn- fix-tx-data
[db tx-data]
(if (some (fn [[op _e a v]]
(= [op a v] [:db/add :db/ident :logseq.class/Root]))
tx-data) ; initial data
tx-data
(->> tx-data
(fix-missing-parent db)
(fix-duplicate-orders db))))
(defn- apply-tx! [^js self sender tx-data]
(let [sql (.-sql self)
conn (.-conn self)
@@ -308,8 +317,7 @@
resolved (de-normalize-tx-data db tx-data)
tx-report (d/with db resolved)
db' (:db-after tx-report)
parent-fixed (fix-missing-parent db' resolved)
order-fixed (fix-duplicate-orders db' parent-fixed)
order-fixed (fix-tx-data db' resolved)
cycle-info (cycle/detect-cycle db' order-fixed)]
(if cycle-info
(do

View File

@@ -42,3 +42,29 @@
(let [tx [{:block/uuid b :block/parent [:block/uuid b]}]
values (cycle/server-values-for @conn tx :block/parent)]
(is (= {(pr-str [:block/uuid b]) [:block/uuid a]} values)))))
(deftest numeric-entity-cycle-test
(let [conn (new-conn)
a (random-uuid)
b (random-uuid)]
(d/transact! conn [{:block/uuid a}
{:block/uuid b :block/parent [:block/uuid a]}])
(let [a-eid (d/entid @conn [:block/uuid a])
tx [[:db/add a-eid :block/parent [:block/uuid b]]]
result (cycle/detect-cycle @conn tx)]
(is (= :block/parent (:attr result)))
(is (= [:block/uuid a] (:entity result))))))
(deftest three-block-cycle-test
(let [conn (new-conn)
a (random-uuid)
b (random-uuid)
c (random-uuid)]
(d/transact! conn [{:block/uuid a}
{:block/uuid b :block/parent [:block/uuid a]}
{:block/uuid c :block/parent [:block/uuid b]}])
(let [a-eid (d/entid @conn [:block/uuid a])
tx [[:db/add a-eid :block/parent [:block/uuid c]]]
result (cycle/detect-cycle @conn tx)]
(is (= :block/parent (:attr result)))
(is (= [:block/uuid a] (:entity result))))))

View File

@@ -130,37 +130,23 @@
(catch :default e
(log/error :worker-sync/apply-remote-tx-failed {:error e})))))
(defn- reconcile-cycle! [repo attr server-values]
(defn- reconcile-cycle! [repo attr server_values]
(when-let [conn (worker-state/get-datascript-conn repo)]
(let [db @conn
tx-data (reduce
(fn [acc [entity-str value]]
(let [entity (reader/read-string entity-str)
eid (d/entid db entity)
current-raw (when eid (get (d/entity db eid) attr))
current (cond
(and (= attr :block/parent) (instance? Entity current-raw))
(when-let [parent-uuid (:block/uuid current-raw)]
[:block/uuid parent-uuid])
(and (= attr :logseq.property.class/extends) (instance? Entity current-raw))
(:db/ident current-raw)
:else current-raw)]
(fn [acc [eid value]]
(let [entity (d/entity db eid)
current (:db/id (get entity attr))]
(cond
(nil? eid) acc
(nil? entity) acc
(nil? value)
(cond
(and current (sequential? current))
(if current
(conj acc [:db/retract eid attr current])
(some? current)
(conj acc [:db/retract eid attr current])
:else acc)
acc)
:else
(conj acc [:db/add eid attr value]))))
[]
server-values)]
server_values)]
(when (seq tx-data)
(d/transact! conn tx-data {:worker-sync/remote? true})))))
@@ -200,9 +186,8 @@
(remove-pending-txs! repo @(:inflight client))))
(reset! (:inflight client) [])
(when (= "cycle" (:reason message))
(let [attr (keyword (:attr message))
server-values (sqlite-util/read-transit-str (:server_values message))]
(reconcile-cycle! repo attr server-values)))
(let [{:keys [attr server_values]} (sqlite-util/read-transit-str (:data message))]
(reconcile-cycle! repo attr server_values)))
(flush-pending! repo client))
"pull/ok" (do
(update-server-t! client (:t message))