fix: large-title rehydrate

Root cause: large-title rehydrate was reusing the remote tx’s
original entity id. When that id was a string tempid,
Datascript had already resolved it during remote apply, so rehydrate
created a new invalid entity with only :block/title.
This commit is contained in:
Tienson Qin
2026-05-08 11:27:15 +08:00
parent 1423a38062
commit c5775df851
2 changed files with 77 additions and 8 deletions

View File

@@ -37,6 +37,20 @@
(string? (:asset-uuid value))
(string? (:asset-type value))))
(defn- find-large-title-object-eid
[db obj]
(some (fn [datom]
(when (= obj (:v datom))
(:e datom)))
(d/datoms db :avet large-title-object-attr)))
(defn- resolve-large-title-item-eid
[db {:keys [e obj]}]
(or (when (number? e)
e)
(some-> (d/entity db e) :db/id)
(find-large-title-object-eid db obj)))
(defn asset-url
[base graph-id asset-uuid asset-type]
(str base "/assets/" graph-id "/" asset-uuid "." asset-type))
@@ -160,12 +174,16 @@
(fail-fast-f :db-sync/missing-field {:repo repo :field :aes-key}))]
(p/all
(mapv (fn [{:keys [e obj]}]
(p/let [title (download-fn repo graph-id* obj aes-key*)]
(ldb/transact! conn*
[[:db/add e :block/title title]]
{:rtc-tx? true
:persist-op? false
:op :large-title-rehydrate})))
(let [eid (resolve-large-title-item-eid @conn* {:e e :obj obj})]
(when-not eid
(fail-fast-f :db-sync/large-title-entity-missing
{:repo repo :e e :obj obj}))
(p/let [title (download-fn repo graph-id* obj aes-key*)]
(ldb/transact! conn*
[[:db/add eid :block/title title]]
{:rtc-tx? true
:persist-op? false
:op :large-title-rehydrate}))))
items)))))))
(defn offload-large-titles-in-datoms-batch

View File

@@ -510,7 +510,8 @@
:ws-state (atom :open)}
broadcasts (atom [])]
(with-redefs [shared-service/broadcast-to-clients! (fn [topic payload]
(swap! broadcasts conj {:topic topic :payload payload}))]
(swap! broadcasts conj {:topic topic :payload payload}))
db-sync/status (constantly {})]
(#'db-sync/update-online-users! client [{:user-id "u1" :username "Alice"}])
(#'db-sync/update-online-users! client [{:user-id "u1" :username "Alice"}])
(is (= 1 (count @broadcasts)))
@@ -774,7 +775,8 @@
:inflight (atom [])
:online-users (atom [])
:ws-state (atom :open)}]
(with-redefs [client-op/get-local-tx (constantly 0)]
(with-redefs [client-op/get-local-tx (constantly 0)
sync-handle-message/sync-counts (constantly {})]
(with-redefs [sync-log-state/rtc-log (fn [type payload]
(reset! *captured {:type type
:payload payload}))]
@@ -4881,6 +4883,55 @@
(is (= "rehydrated-title" (:block/title block))))
(p/finally done))))))))
(deftest rehydrate-large-title-tempid-test
(testing "rehydrate resolves remote tx tempids to the applied entity"
(async done
(let [conn (db-test/create-conn-with-blocks
{:pages-and-blocks
[{:page {:block/title "tempid-rehydrate-page"}}]})
page (db-test/find-page-by-title @conn "tempid-rehydrate-page")
page-id (:db/id page)
block-uuid (random-uuid)
tempid (str block-uuid)
obj {:asset-uuid "title-tempid" :asset-type "txt"}
tx-data [[:db/add tempid :block/uuid block-uuid]
[:db/add tempid :block/title ""]
[:db/add tempid :block/page page-id]
[:db/add tempid :block/parent page-id]
[:db/add tempid :block/order "a0"]
[:db/add tempid :block/created-at 1]
[:db/add tempid :block/updated-at 1]
[:db/add tempid :logseq.property.sync/large-title-object obj]]
download-calls (atom [])
download-fn (fn [_repo _graph-id obj* _aes-key]
(swap! download-calls conj obj*)
(p/resolved "rehydrated tempid title"))]
(with-datascript-conns conn nil
(fn []
(d/transact! conn tx-data)
(let [block-id (:db/id (d/entity @conn [:block/uuid block-uuid]))]
(is (int? block-id))
(-> (p/let [_ (sync-large-title/rehydrate-large-titles!
test-repo
{:tx-data tx-data
:conn conn
:graph-id "graph-1"
:download-fn download-fn
:aes-key nil
:get-conn-f worker-state/get-datascript-conn
:graph-e2ee?-f sync-crypt/graph-e2ee?
:ensure-graph-aes-key-f sync-crypt/<ensure-graph-aes-key
:fail-fast-f db-sync/fail-fast})
block (d/entity @conn [:block/uuid block-uuid])
tempid-ent (d/entity @conn tempid)]
(is (= [obj] @download-calls))
(is (= block-id (:db/id block)))
(is (= "rehydrated tempid title" (:block/title block)))
(is (nil? tempid-ent)))
(p/catch (fn [e]
(is false (str e))))
(p/finally done)))))))))
(defn- apply-template-to-empty-target!
[conn template-root-uuid empty-target-uuid]
(let [template-root (d/entity @conn [:block/uuid template-root-uuid])