From 98e262d118050aec3039b395e2af152c3c121fc0 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 12 Mar 2026 11:53:57 +0800 Subject: [PATCH] fix: reduce memory usage when encrypting large graph --- src/main/frontend/worker/sync.cljs | 155 +++++++++++++++++---- src/main/frontend/worker/sync/crypt.cljs | 37 +++-- src/test/frontend/worker/db_sync_test.cljs | 59 ++++++++ 3 files changed, 211 insertions(+), 40 deletions(-) diff --git a/src/main/frontend/worker/sync.cljs b/src/main/frontend/worker/sync.cljs index 15d0e8da19..26610b952a 100644 --- a/src/main/frontend/worker/sync.cljs +++ b/src/main/frontend/worker/sync.cljs @@ -28,6 +28,7 @@ (defonce *repo->latest-remote-tx (atom {})) (defonce *start-inflight-target (atom nil)) +(defonce ^:private *upload-temp-opfs-pool (atom nil)) (defn- current-client [repo] @@ -171,7 +172,9 @@ opts)) (def ^:private max-asset-size (* 100 1024 1024)) -(def ^:private upload-kvs-batch-size 500) +(def ^:private upload-kvs-batch-size 2000) +(def ^:private upload-prepare-datoms-batch-size 100000) +(def ^:private upload-temp-pool-name (worker-util/get-pool-name "upload-temp")) (def ^:private snapshot-content-type "application/transit+json") (def ^:private snapshot-content-encoding "gzip") (def ^:private snapshot-text-encoder (js/TextEncoder.)) @@ -449,29 +452,65 @@ (-restore [_ addr] (restore-data-from-addr db addr)))) -(defn- create-temp-sqlite-db +(defn- (p/let [^js root (.getDirectory js/navigator.storage) + ^js dir (.getDirectoryHandle root (str "." upload-temp-pool-name))] + (.removeEntry dir (subs path 1))) + (p/catch + (fn [error] + (if (= "NotFoundError" (.-name error)) + nil + (p/rejected error)))))) (defn- cleanup-temp-sqlite! - [{:keys [db conn]}] + [{:keys [db conn path]}] (when conn (reset! conn nil)) (when db - (.close db))) + (.close db)) + (when path + (= n batch-size)) + [(persistent! batch) remaining] + (recur (conj! batch (first remaining)) + (next remaining) + (inc n))))) + +(defn- datom->tx + [datom] + [:db/add (:e datom) (:a datom) (:v datom)]) + +(defn- tx encrypted-datoms)] + (d/transact! (:conn temp) tx-data {:initial-db? true}) + nil)) + :progress-f + (fn [processed total] + (update-progress {:sub-type :upload-progress + :message (if aes-key + (str "Encrypting " processed "/" total) + (str "Preparing " processed "/" total))}))})] + temp)) + (defn rehydrate-large-titles-from-db! [repo graph-id] (when-let [conn (worker-state/get-datascript-conn repo)] @@ -1962,15 +2073,9 @@ (fail-fast :db-sync/missing-field {:repo repo :field :aes-key}))] (set-graph-sync-metadata! repo graph-e2ee?) (ensure-client-graph-uuid! repo graph-id) - (p/let [datoms (d/datoms @source-conn :eavt) - _ (prn :debug :datoms-count (count datoms) :time (js/Date.)) - datoms* (offload-large-titles-in-datoms repo graph-id datoms aes-key) - _ (update-progress {:sub-type :upload-progress - :message (if graph-e2ee? "Encrypting data" "Preparing data")}) - encrypted-datoms (if graph-e2ee? - (sync-crypt/ (p/loop [last-addr -1 diff --git a/src/main/frontend/worker/sync/crypt.cljs b/src/main/frontend/worker/sync/crypt.cljs index 670cc8a07c..18ce0d5c03 100644 --- a/src/main/frontend/worker/sync/crypt.cljs +++ b/src/main/frontend/worker/sync/crypt.cljs @@ -504,21 +504,28 @@ (p/all (map #( (p/let [_ (#'db-sync/ (p/let [{:keys [db path]} (#'db-sync/ (p/let [_ (#'db-sync/cleanup-temp-sqlite! + {:db #js {:close (fn [] (reset! closed? true))} + :path "/upload-temp.sqlite"})] + (is @closed?) + (is (= ["/upload-temp.sqlite"] @removed-paths))) + (p/finally done))))))) + (deftest ^:long upload-large-title-encrypts-transit-payload-test (testing "encrypted large title uploads transit-encoded payload" (async done