From ebbb3222d5d773379a8b780f9dcda7bd8e09c870 Mon Sep 17 00:00:00 2001 From: rcmerci Date: Sun, 19 Jan 2025 15:43:37 +0800 Subject: [PATCH] enhance(rtc): validate major-schema-version before rtc --- deps/db/src/logseq/db.cljs | 4 ++ deps/db/src/logseq/db/frontend/kv_entity.cljs | 4 ++ .../frontend/worker/rtc/branch_graph.cljs | 3 +- src/main/frontend/worker/rtc/core.cljs | 67 ++++++++++++++----- src/main/frontend/worker/rtc/exception.cljs | 13 ++-- 5 files changed, 67 insertions(+), 24 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 3ec7afc845..08a91705a4 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -528,6 +528,10 @@ [db] (when db (get-key-value db :logseq.kv/schema-version))) +(defn get-graph-remote-schema-version + [db] + (when db (get-key-value db :logseq.kv/remote-schema-version))) + ;; File based fns (defn get-namespace-pages "Accepts both sanitized and unsanitized namespaces" diff --git a/deps/db/src/logseq/db/frontend/kv_entity.cljs b/deps/db/src/logseq/db/frontend/kv_entity.cljs index 1d078c759e..e9e794c904 100644 --- a/deps/db/src/logseq/db/frontend/kv_entity.cljs +++ b/deps/db/src/logseq/db/frontend/kv_entity.cljs @@ -14,6 +14,10 @@ :rtc {:rtc/ignore-entity-when-init-upload true :rtc/ignore-entity-when-init-download true}} :logseq.kv/schema-version {:doc "Graph's current schema version"} + :logseq.kv/remote-schema-version {:doc "Graph's remote schema version. +RTC won't start when (not= local-schema-version remote-schema-version)" + :rtc {:rtc/ignore-entity-when-init-upload true + :rtc/ignore-entity-when-init-download true}} :logseq.kv/graph-created-at {:doc "Graph's created at time"} :logseq.kv/latest-code-lang {:doc "Latest lang used by a #Code-block" :rtc {:rtc/ignore-entity-when-init-upload true diff --git a/src/main/frontend/worker/rtc/branch_graph.cljs b/src/main/frontend/worker/rtc/branch_graph.cljs index c483dc9667..892eee6ced 100644 --- a/src/main/frontend/worker/rtc/branch_graph.cljs +++ b/src/main/frontend/worker/rtc/branch_graph.cljs @@ -26,7 +26,7 @@ (defn compare-schemas "Return one of [:create-branch :download nil]. when nil, nothing need to do" - [server-graph-state server-graph-schema app-schema client-graph-schema] + [server-graph-schema app-schema client-graph-schema] (let [[server-graph-schema app-schema client-graph-schema] (map major-version [server-graph-schema app-schema client-graph-schema])] (cond @@ -47,6 +47,5 @@ (cond ;; this remote-graph branch is creating now, ;; disallow upload a new schema-version graph for now - (= "creating" server-graph-state) nil (>= server-graph-schema app-schema) nil (< server-graph-schema app-schema) :create-branch)))) diff --git a/src/main/frontend/worker/rtc/core.cljs b/src/main/frontend/worker/rtc/core.cljs index 2c86ac7864..ade30c67e4 100644 --- a/src/main/frontend/worker/rtc/core.cljs +++ b/src/main/frontend/worker/rtc/core.cljs @@ -2,8 +2,10 @@ "Main(use missionary) ns for rtc related fns" (:require [clojure.data :as data] [datascript.core :as d] + [frontend.common.missionary :as c.m] [frontend.worker.device :as worker-device] [frontend.worker.rtc.asset :as r.asset] + [frontend.worker.rtc.branch-graph :as r.branch-graph] [frontend.worker.rtc.client :as r.client] [frontend.worker.rtc.client-op :as client-op] [frontend.worker.rtc.exception :as r.ex] @@ -16,8 +18,8 @@ [frontend.worker.state :as worker-state] [frontend.worker.util :as worker-util] [logseq.common.config :as common-config] - [frontend.common.missionary :as c.m] [logseq.db :as ldb] + [logseq.db.frontend.schema :as db-schema] [malli.core :as ma] [missionary.core :as m]) (:import [missionary Cancelled])) @@ -264,18 +266,52 @@ (fn [v] (= (set (keys empty-rtc-loop-metadata)) (set (keys v)))))) +(defn- validate-rtc-start-conditions + [repo token] + (if-let [conn (worker-state/get-datascript-conn repo)] + (let [user-uuid (:sub (worker-util/parse-jwt token)) + graph-uuid (ldb/get-graph-rtc-uuid @conn) + schema-version (ldb/get-graph-schema-version @conn) + remote-schema-version (ldb/get-graph-remote-schema-version @conn) + app-schema-version db-schema/version] + (cond + (not user-uuid) + (ex-info "Invalid token" {:type :rtc.exception/invalid-token}) + + (not graph-uuid) + r.ex/ex-local-not-rtc-graph + + (not schema-version) + (ex-info "Not found schema-version" {:type :rtc.exception/not-found-schema-version}) + + (not remote-schema-version) + (ex-info "Not found remote-schema-version" {:type :rtc.exception/not-found-remote-schema-version}) + + (apply not= (map r.branch-graph/major-version [app-schema-version remote-schema-version schema-version])) + (ex-info "major schema version mismatch" {:type :rtc.exception/major-schema-version-mismatched + :local schema-version + :remote remote-schema-version}) + :else + {:conn conn + :user-uuid user-uuid + :graph-uuid graph-uuid + :schema-version schema-version + :remote-schema-version remote-schema-version + :date-formatter (common-config/get-date-formatter (worker-state/get-config repo))})) + (ex-info "Not found db-conn" {:type :rtc.exception/not-found-db-conn + :repo repo}))) + ;;; ================ API ================ (defn new-task--rtc-start [repo token] (m/sp - ;; ensure device metadata existing first + ;; ensure device metadata existing first (m/? (worker-device/new-task--ensure-device-metadata! token)) - (if-let [conn (worker-state/get-datascript-conn repo)] - (if-let [graph-uuid (ldb/get-graph-rtc-uuid @conn)] - (let [user-uuid (:sub (worker-util/parse-jwt token)) - config (worker-state/get-config repo) - date-formatter (common-config/get-date-formatter config) - {:keys [rtc-state-flow *rtc-auto-push? *rtc-remote-profile? rtc-loop-task *online-users onstarted-task]} + (let [{:keys [conn user-uuid graph-uuid _schema-version _remote-schema-version date-formatter] :as r} + (validate-rtc-start-conditions repo token)] + (if (instance? ExceptionInfo r) + (r.ex/->map r) + (let [{:keys [rtc-state-flow *rtc-auto-push? *rtc-remote-profile? rtc-loop-task *online-users onstarted-task]} (create-rtc-loop graph-uuid repo conn date-formatter token) *last-stop-exception (atom nil) canceler (c.m/run-task rtc-loop-task :rtc-loop-task @@ -295,10 +331,7 @@ :*rtc-lock *rtc-lock :canceler canceler :*last-stop-exception *last-stop-exception}) - nil))) - (r.ex/->map r.ex/ex-local-not-rtc-graph)) - (r.ex/->map (ex-info "Not found db-conn" {:type :rtc.exception/not-found-db-conn - :repo repo}))))) + nil))))))) (defn rtc-stop [] @@ -434,11 +467,11 @@ ;;; subscribe state ;;; (when-not common-config/PUBLISHING - (c.m/run-background-task - ::subscribe-state - (m/reduce - (fn [_ v] (worker-util/post-message :rtc-sync-state v)) - create-get-state-flow))) + (c.m/run-background-task + ::subscribe-state + (m/reduce + (fn [_ v] (worker-util/post-message :rtc-sync-state v)) + create-get-state-flow))) (comment (do diff --git a/src/main/frontend/worker/rtc/exception.cljs b/src/main/frontend/worker/rtc/exception.cljs index ee43472670..6a9826873e 100644 --- a/src/main/frontend/worker/rtc/exception.cljs +++ b/src/main/frontend/worker/rtc/exception.cljs @@ -8,16 +8,19 @@ :rtc.exception/remote-graph-lock-missing {:doc " Remote exception. Failed to remote graph lock isn't exist. It's a server internal error, shouldn't happen."} + :rtc.exception/invalid-token {:doc "Local exception"} :rtc.exception/not-rtc-graph {:doc "Local exception. Trying to start rtc loop on a local-graph."} - :rtc.exception/lock-failed {:doc " -Local exception. + :rtc.exception/lock-failed {:doc "Local exception. Trying to start rtc loop but there's already one running, need to cancel that one first."} :rtc.exception/not-found-db-conn {:doc "Local exception. Cannot find db-conn by repo"} - :rtc.exception/get-s3-object-failed {:doc " -Failed to fetch response from s3. + :rtc.exception/not-found-schema-version {:doc "Local exception. graph doesn't have :logseq.kv/schema-version value"} + :rtc.exception/not-found-remote-schema-version{:doc "Local exception. +graph doesn't have :logseq.kv/remote-schema-version value"} + :rtc.exception/major-schema-version-mismatched {:doc "Local exception. +local-schema-version not equal to remote-schema-version, cannot start rtc"} + :rtc.exception/get-s3-object-failed {:doc "Failed to fetch response from s3. When response from remote is too huge(> 32KB), the server will put it to s3 and return its presigned-url to clients."} - :rtc.exception/different-graph-skeleton {:doc "remote graph skeleton data is different from local's."} :rtc.exception/bad-request-body {:doc "bad request body, rejected by server-schema"} :rtc.exception/not-allowed {:doc "this api-call is not allowed"} :rtc.exception/ws-timeout {:doc "websocket timeout"})