Performance improvement for inserting new blocks

pull/645/head
Tienson Qin 2020-09-07 23:26:36 +08:00
parent f961d369c1
commit 60b04b0d5d
8 changed files with 186 additions and 86 deletions

View File

@ -17,7 +17,8 @@
cljs-drag-n-drop {:mvn/version "0.1.0"}
borkdude/sci {:mvn/version "0.1.1-alpha.6"}
hickory {:mvn/version "0.7.1"}
hiccups {:mvn/version "0.3.0"}}
hiccups {:mvn/version "0.3.0"}
org.clojure/core.async {:mvn/version "1.3.610"}}
:aliases {:cljs {:extra-paths ["src/dev-cljs/"]
:extra-deps {org.clojure/clojurescript {:mvn/version "1.10.520"}

View File

@ -177,6 +177,9 @@
(notification/show! "No changed files yet!" :warning)))))}
(fn [e key-code]
nil))))
{:did-mount (fn [state]
(handler/set-save-before-unload!)
state)}
(mixins/keyboards-mixin keyboards/keyboards)
[state route-match main-content]
(let [{:keys [open? close-fn open-fn]} state

View File

@ -24,7 +24,8 @@
[frontend.util :as util :refer-macros [profile]]
[frontend.extensions.sci :as sci]
[goog.array :as garray]
[frontend.db-schema :as db-schema]))
[frontend.db-schema :as db-schema]
[clojure.core.async :as async]))
(defonce brain "🧠")
(defonce brain-text "logseq-second-brain")
@ -145,6 +146,10 @@
:transform-fn transform-fn})
result-atom)
(defn get-page-blocks-cache-atom
[repo page-id]
(:result (get @query-state [repo :page/blocks page-id])))
(defn remove-q!
[k]
(swap! query-state dissoc k))
@ -480,6 +485,13 @@
(group-by-page result)))
result)))
(defn get-repo-tx-id [repo]
(when-let [db (get-conn repo)]
))
(defn get-tx-id [tx-report]
(get-in tx-report [:tempids :db/current-tx]))
(defn transact!
([tx-data]
(transact! (state/get-current-repo) tx-data))
@ -488,7 +500,8 @@
(remove nil?))]
(when (seq tx-data)
(when-let [conn (get-conn repo-url false)]
(d/transact! conn (vec tx-data)))))))
(let [tx-report (d/transact! conn (vec tx-data))]
(state/mark-repo-as-changed! repo-url (get-tx-id tx-report))))))))
(defn get-key-value
([key]
@ -518,6 +531,7 @@
(get-conn repo-url false)))]
(when (and (seq tx-data) (get-conn))
(let [tx-result (profile "Transact!" (d/transact! (get-conn) (vec tx-data)))
_ (state/mark-repo-as-changed! repo-url (get-tx-id tx-result))
db (:db-after tx-result)
handler-keys (get-handler-keys handler-opts)]
(doseq [handler-key handler-keys]
@ -1162,16 +1176,16 @@
page-list (when-let [list-content (:list directives)]
(extract-page-list list-content))]
(cond->
(util/remove-nils
{:page/name (string/lower-case page)
:page/original-name page
:page/file [:file/path file]
:page/journal? journal?
:page/journal-day (if journal?
(date/journal-title->int (string/capitalize page))
0)
:page/created-at journal-date-long
:page/last-modified-at journal-date-long})
(util/remove-nils
{:page/name (string/lower-case page)
:page/original-name page
:page/file [:file/path file]
:page/journal? journal?
:page/journal-day (if journal?
(date/journal-title->int (string/capitalize page))
0)
:page/created-at journal-date-long
:page/last-modified-at journal-date-long})
(seq directives)
(assoc :page/directives directives)
@ -1315,8 +1329,8 @@
file-content)
tx (concat tx [(let [t (tc/to-long (t/now))]
(cond->
{:file/path file
:file/last-modified-at t}
{:file/path file
:file/last-modified-at t}
new?
(assoc :file/created-at t)))])]
(transact! repo-url tx))))
@ -1642,20 +1656,18 @@
config)))
(defn start-db-conn!
[me repo listen-handler!]
[me repo]
(let [files-db-name (datascript-files-db repo)
files-db-conn (d/create-conn db-schema/files-db-schema)
db-name (datascript-db repo)
db-conn (d/create-conn db-schema/schema)]
(swap! conns assoc files-db-name files-db-conn)
(swap! conns assoc db-name db-conn)
(when listen-handler!
(listen-handler! repo db-conn))
(when me
(d/transact! db-conn [(me-tx (d/db db-conn) me)]))))
(defn restore!
[{:keys [repos] :as me} listen-handler restore-config-handler]
[{:keys [repos] :as me} restore-config-handler]
(let [logged? (:name me)]
(doall
(for [{:keys [url]} repos]
@ -1685,8 +1697,7 @@
(reset-conn! db-conn attached-db)))
(when logged?
(d/transact! db-conn [(me-tx (d/db db-conn) me)])))
_ (restore-config-handler repo)]
(when stored (listen-handler repo db-conn)))))))))
_ (restore-config-handler repo)])))))))
(defn- build-edges
[edges]
@ -1706,13 +1717,13 @@
[dark? current-page edges nodes]
(mapv (fn [p]
(cond->
{:id p
:name p
:val (get-connections p edges)
:autoColorBy "group"
:group (js/Math.ceil (* (js/Math.random) 12))
:color "#222222"
}
{:id p
:name p
:val (get-connections p edges)
:autoColorBy "group"
:group (js/Math.ceil (* (js/Math.random) 12))
:color "#222222"
}
dark?
(assoc :color "#8abbbb")
(= p current-page)
@ -1850,7 +1861,7 @@
(defn blocks->vec-tree [col]
(let [col (map (fn [h] (cond->
h
h
(not (:block/dummy? h))
(dissoc h :block/meta))) col)
parent? (fn [item children]
@ -2110,6 +2121,14 @@
(and (= typ "Paragraph")
(every? #(= % ["Break_Line"]) break-lines))) (rest ast))))))))
(defn run-batch-txs!
[]
(let [chan (state/get-db-batch-txs-chan)]
(async/go-loop []
(let [f (async/<! chan)]
(f))
(recur))))
(comment
(defn debug!
[]

View File

@ -33,7 +33,6 @@
(js/clearInterval @interval)
(reset! interval nil)
(-> (p/all (db/restore! (assoc me :repos repos)
repo-handler/db-listen-to-tx!
(fn [repo]
(file-handler/restore-config! repo false)
(when (and (state/logged?)
@ -60,6 +59,35 @@
(let [interval-id (js/setInterval inner-fn 50)]
(reset! interval interval-id))))
(defn persist-repo-to-indexeddb!
([]
(persist-repo-to-indexeddb! false))
([force?]
(let [status (state/get-repo-persist-status)]
(doseq [[repo {:keys [last-stored-at last-modified-at] :as repo-status}] status]
(when (and (> last-modified-at last-stored-at)
(or force?
(and (state/get-edit-input-id)
(> (- (util/time-ms) last-stored-at) (* 1 60 1000)) ; 5 minutes
)
(nil? (state/get-edit-input-id))))
(p/let [_ (repo-handler/persist-repo! repo)]
(state/update-repo-last-stored-at! repo)))))))
(defn periodically-persist-repo-to-indexeddb!
[]
(js/setInterval persist-repo-to-indexeddb! (* 5 1000)))
(defn set-save-before-unload! []
(.addEventListener js/window "beforeunload"
(fn [e]
(when (state/repos-need-to-be-stored?)
;; FIXME: Not working
(persist-repo-to-indexeddb! true)
(let [message "\\o/"]
(set! (.-returnValue (or e js/window.event)) message)
message)))))
(defn start!
[render]
(let [me (and js/window.user (bean/->clj js/window.user))
@ -76,4 +104,8 @@
(notification/show! "Sorry, it seems that your browser doesn't support IndexedDB, we recommend to use latest Chrome(Chromium) or Firefox(Non-private mode)." :error false)
(state/set-indexedb-support? false)))
(restore-and-setup! me repos logged?)))
(restore-and-setup! me repos logged?)
(periodically-persist-repo-to-indexeddb!)
(db/run-batch-txs!)))

View File

@ -37,7 +37,8 @@
*show-block-commands]]
[frontend.extensions.html-parser :as html-parser]
[medley.core :as medley]
[frontend.text :as text]))
[frontend.text :as text]
[clojure.core.async :as async]))
;; TODO: refactor the state, it is already too complex.
(defonce *last-edit-block (atom nil))
@ -481,22 +482,31 @@
value)
[new-content value] (new-file-content block file-content value)
{:keys [blocks pages start-pos end-pos]} (block/parse-block (assoc block :block/content value) format)
first-block (first blocks)
last-block (last blocks)
blocks (db/recompute-block-children repo block blocks)
after-blocks (rebuild-after-blocks repo file (:end-pos meta) end-pos)]
(repo-handler/transact-react-and-alter-file!
repo
(concat
pages
blocks
after-blocks)
{:key :block/change
:data (map (fn [block] (assoc block :block/page page)) blocks)}
[[file-path new-content]])
after-blocks (rebuild-after-blocks repo file (:end-pos meta) end-pos)
blocks-atom (db/get-page-blocks-cache-atom repo (:db/id page))
[before-part after-part] (split-with #(not= (:block/uuid (first blocks)) (:block/uuid %)) @blocks-atom)
after-part (rest after-part)]
(reset! blocks-atom (->> (concat before-part blocks after-part)
(remove nil?)))
;; Replace with batch transactions
(state/add-tx!
(fn []
(repo-handler/transact-react-and-alter-file!
repo
(concat
pages
blocks
after-blocks)
{:key :block/change
:data (map (fn [block] (assoc block :block/page page)) blocks)}
[[file-path new-content]])))
(when ok-handler
(ok-handler [first-block last-block v2]))))]
(let [first-block (first blocks)
last-block (last blocks)]
(ok-handler [first-block last-block v2])))))]
(cond
(and (not file) page)
(let [format (name format)
@ -562,20 +572,22 @@
:pos pos}))
(defn edit-block!
[block-id prev-pos format id]
(let [edit-input-id (str (subs id 0 (- (count id) 36)) block-id)
block (or
(db/pull [:block/uuid block-id])
;; dummy?
{:block/uuid block-id
:block/content ""})
{:block/keys [content]} block
content (string/trim (text/remove-level-spaces content format))
content-length (count content)
text-range (if (or (= :max prev-pos) (<= content-length prev-pos))
content
(subs content 0 prev-pos))]
(state/set-editing! edit-input-id content block text-range)))
[block prev-pos format id]
(when-let [block-id (:block/uuid block)]
(let [edit-input-id (str (subs id 0 (- (count id) 36)) block-id)
block (or
block
(db/pull [:block/uuid block-id])
;; dummy?
{:block/uuid block-id
:block/content ""})
{:block/keys [content]} block
content (string/trim (text/remove-level-spaces content format))
content-length (count content)
text-range (if (or (= :max prev-pos) (<= content-length prev-pos))
content
(subs content 0 prev-pos))]
(state/set-editing! edit-input-id content block text-range))))
(defn- insert-new-block!
[state]
@ -598,7 +610,7 @@
true
(fn [[_first-block last-block _new-block-content]]
(let [last-id (:block/uuid last-block)]
(edit-block! last-id 0 format id)
(edit-block! last-block 0 format id)
(clear-when-saved!)))
(if last-child true false)
(and last-child
@ -734,8 +746,8 @@
0)
0)]
(save-block-if-changed! block new-value)
(edit-block! (uuid sibling-block-id)
pos format id)))))))))))))
(let [block (db/pull (state/get-current-repo) '[*] [:block/uuid (uuid sibling-block-id)])]
(edit-block! block pos format id))))))))))))))
(defn delete-blocks!
[repo block-uuids]
@ -1052,7 +1064,8 @@
(when (not= (string/trim (text/remove-level-spaces content format))
(string/trim value))
(save-block! state (:value state))))
(edit-block! (uuid sibling-block-id) pos format id)))))))))
(let [block (db/pull (state/get-current-repo) '[*] [:block/uuid (uuid sibling-block-id)])]
(edit-block! block pos format id))))))))))
(defn insert-command!
[id command-output format {:keys [restore?]

View File

@ -52,7 +52,7 @@
last-block (last blocks)]
(js/setTimeout
#(when-let [first-block (util/get-first-block-by-id (:block/uuid last-block))]
(editor-handler/edit-block! (:block/uuid last-block)
(editor-handler/edit-block! last-block
0
(:block/format last-block)
(string/replace (gobj/get first-block "id")

View File

@ -263,27 +263,18 @@
(create-today-journal-if-not-exists repo-url))
(create-contents-file repo-url)))
(defn db-listen-to-tx!
[repo db-conn]
(defn persist-repo!
[repo]
(when-let [files-conn (db/get-files-conn repo)]
(d/listen! files-conn :persistence
(fn [tx-report]
(when (seq (:tx-data tx-report))
(when-let [db (:db-after tx-report)]
(db/persist repo db true))))))
(d/listen! db-conn :persistence
(fn [tx-report]
(when (seq (:tx-data tx-report))
(when-let [db (:db-after tx-report)]
(db/persist repo db false))))))
(db/persist repo @files-conn true))
(when-let [db (db/get-conn repo)]
(prn "persisted core db")
(db/persist repo db false)))
(defn load-db-and-journals!
[repo-url diffs first-clone?]
(when (or diffs first-clone?)
(p/let [_ (load-repo-to-db! repo-url diffs first-clone?)]
(when-let [conn (db/get-conn repo-url false)]
(db-listen-to-tx! repo-url conn))
(when first-clone?
(create-default-files! repo-url))
@ -468,7 +459,7 @@
(fn [result]
(state/set-git-clone-repo! "")
(state/set-current-repo! repo-url)
(db/start-db-conn! (:me @state/state) repo-url nil)
(db/start-db-conn! (:me @state/state) repo-url)
(db/mark-repo-as-cloned repo-url)
(git-handler/set-latest-commit-if-exists! repo-url))
(fn [e]
@ -519,7 +510,8 @@
(db/remove-files-db! url)
(fs/rmdir (util/get-repo-dir url))
(state/delete-repo! repo)
(state/clear-changed-files! repo))
(state/clear-changed-files! repo)
)
(fn [error]
(prn "Delete repo failed, error: " error))))
@ -530,7 +522,7 @@
(p/let [result (-> (fs/mkdir (str "/" repo))
(p/catch (fn [_e] nil)))
_ (state/set-current-repo! repo)
_ (db/start-db-conn! nil repo db-listen-to-tx!)
_ (db/start-db-conn! nil repo)
_ (create-month-journal-if-not-exists repo)
_ (create-config-file-if-not-exists repo)
_ (create-contents-file repo)]

View File

@ -6,12 +6,16 @@
[medley.core :as medley]
[goog.object :as gobj]
[goog.dom :as gdom]
[dommy.core :as dom]))
[dommy.core :as dom]
[cljs-time.core :as t]
[cljs-time.coerce :as tc]
[clojure.core.async :as async]))
(defonce state
(atom
{:route-match nil
:today nil
:db/batch-txs (async/chan 100)
:notification/show? false
:notification/content nil
:repo/cloning? false
@ -22,6 +26,11 @@
(storage/get "git-changed-files")
{})
:indexeddb/support? true
;; TODO: save in local storage so that if :changed? is true when user
;; reloads the browser, the app should re-index the repo (another way
;; is to save all the tx data since :last-stored-at)
;; repo -> {:last-stored-at :last-modified-at}
:repo/persist-status {}
:me nil
:git/clone-repo (or (storage/get :git/clone-repo) "")
:git/current-repo (storage/get :git/current-repo)
@ -413,9 +422,9 @@
repos (get-in @state [:me :repos])]
(when (seq repos)
(let [repos (mapv (fn [{:keys [installation_id] :as r}]
(if-let [token (get tokens installation_id)]
(assoc r :token token)
r)) repos)]
(if-let [token (get tokens installation_id)]
(assoc r :token token)
r)) repos)]
(swap! state assoc-in [:me :repos] repos))))))
(defn get-github-token
@ -651,3 +660,34 @@
(when-let [basis (get-in @state [:config repo :journal-basis])]
(keyword (string/lower-case (str basis)))))
:monthly))
(defn update-repo-last-stored-at!
[repo]
(swap! state assoc-in [:repo/persist-status repo :last-stored-at] (util/time-ms)))
(defn get-repo-persist-status
[]
(:repo/persist-status @state))
(defn mark-repo-as-changed!
[repo _tx-id]
(swap! state assoc-in [:repo/persist-status repo :last-modified-at] (util/time-ms)))
(defn add-tx!
;; TODO: replace f with data for batch transactions
[f]
(when f
(swap! state update :db/batch-txs (fn [chan]
(async/put! chan f)
chan))))
(defn get-db-batch-txs-chan
[]
(:db/batch-txs @state))
(defn repos-need-to-be-stored?
[]
(let [status (vals (get-repo-persist-status))]
(some (fn [{:keys [last-stored-at last-modified-at]}]
(> last-modified-at last-stored-at))
status)))