|
3 | 3 | (:require [electron.ipc :as ipc] |
4 | 4 | [frontend.config :as config] |
5 | 5 | [frontend.db.transact :as db-transact] |
| 6 | + [frontend.handler.graph-failover :as graph-failover] |
6 | 7 | [frontend.persist-db.browser :as browser] |
7 | 8 | [frontend.persist-db.protocol :as protocol] |
8 | 9 | [frontend.persist-db.remote :as remote] |
|
13 | 14 | [logseq.db :as ldb] |
14 | 15 | [promesa.core :as p])) |
15 | 16 |
|
| 17 | +(def max-db-worker-request-failures 3) |
| 18 | + |
16 | 19 | (defonce opfs-db (browser/->InBrowser)) |
17 | 20 | (defonce remote-db (atom nil)) |
18 | 21 | (defonce remote-repo (atom nil)) |
| 22 | +(defonce remote-runtime-state (atom nil)) |
19 | 23 |
|
20 | 24 | (defn- clear-remote-runtime! |
21 | 25 | [] |
| 26 | + (reset! remote-runtime-state nil) |
22 | 27 | (reset! remote-db nil) |
23 | 28 | (reset! remote-repo nil) |
24 | 29 | (reset! state/*db-worker nil)) |
|
37 | 42 | (p/resolved true))) |
38 | 43 | (p/resolved false))) |
39 | 44 |
|
| 45 | +(defn- set-remote-runtime! |
| 46 | + [repo client session-id] |
| 47 | + (reset! remote-runtime-state {:repo repo |
| 48 | + :client client |
| 49 | + :session-id session-id |
| 50 | + :request-failures 0 |
| 51 | + :failover-triggered? false}) |
| 52 | + (reset! remote-db client) |
| 53 | + (reset! remote-repo repo) |
| 54 | + (reset! state/*db-worker (:wrapped-worker client))) |
| 55 | + |
| 56 | +(defn- active-runtime-session? |
| 57 | + [state repo session-id] |
| 58 | + (and (= repo (:repo state)) |
| 59 | + (= session-id (:session-id state)))) |
| 60 | + |
| 61 | +(defn- reset-active-request-failures! |
| 62 | + [repo session-id] |
| 63 | + (swap! remote-runtime-state |
| 64 | + (fn [state] |
| 65 | + (if (active-runtime-session? state repo session-id) |
| 66 | + (assoc state :request-failures 0) |
| 67 | + state)))) |
| 68 | + |
| 69 | +(defn- server-unavailable-error? |
| 70 | + [error] |
| 71 | + (let [{:keys [status code]} (ex-data error)] |
| 72 | + (or (nil? (ex-data error)) |
| 73 | + (= :server-unavailable code) |
| 74 | + (= :db-worker-unavailable code) |
| 75 | + (= :connection-refused code) |
| 76 | + (= :fetch-failed code) |
| 77 | + (= :network-error code) |
| 78 | + (= 0 status)))) |
| 79 | + |
| 80 | +(defn- trigger-db-worker-failover! |
| 81 | + [repo remote-client] |
| 82 | + (when remote-client |
| 83 | + (-> (remote/stop! remote-client) |
| 84 | + (p/catch (fn [error] |
| 85 | + (log/warn :db-worker-failover-stop-error {:repo repo |
| 86 | + :error error}))))) |
| 87 | + (when (= repo @remote-repo) |
| 88 | + (clear-remote-runtime!)) |
| 89 | + (-> (ipc/ipc "releaseDbWorkerRuntime" repo) |
| 90 | + (p/catch (fn [error] |
| 91 | + (log/warn :db-worker-failover-release-runtime-error {:repo repo |
| 92 | + :error error})))) |
| 93 | + (graph-failover/switch-away-from-current-repo! repo {:reason :db-worker-request-failed})) |
| 94 | + |
| 95 | +(defn- record-active-request-failure! |
| 96 | + [repo session-id error] |
| 97 | + (when (server-unavailable-error? error) |
| 98 | + (let [triggered? (atom false) |
| 99 | + remote-client (atom nil)] |
| 100 | + (swap! remote-runtime-state |
| 101 | + (fn [state] |
| 102 | + (if (and (active-runtime-session? state repo session-id) |
| 103 | + (not (:failover-triggered? state))) |
| 104 | + (let [failures (inc (or (:request-failures state) 0))] |
| 105 | + (if (>= failures max-db-worker-request-failures) |
| 106 | + (do |
| 107 | + (reset! triggered? true) |
| 108 | + (reset! remote-client (:client state)) |
| 109 | + (assoc state |
| 110 | + :request-failures failures |
| 111 | + :failover-triggered? true)) |
| 112 | + (assoc state :request-failures failures))) |
| 113 | + state))) |
| 114 | + (when @triggered? |
| 115 | + (trigger-db-worker-failover! repo @remote-client))))) |
| 116 | + |
40 | 117 | (defn- node-runtime? |
41 | 118 | [] |
42 | 119 | (and (exists? js/process) |
|
57 | 134 | [repo] |
58 | 135 | (if (or (nil? repo) (= repo @remote-repo)) |
59 | 136 | (p/resolved @remote-db) |
60 | | - (p/let [_ (when @remote-db |
61 | | - (remote/stop! @remote-db)) |
62 | | - runtime (ipc/ipc "db-worker-runtime" repo) |
63 | | - client (remote/start! (assoc runtime |
64 | | - :repo repo |
65 | | - :event-handler worker-handler/handle))] |
66 | | - (reset! remote-db client) |
67 | | - (reset! remote-repo repo) |
68 | | - (reset! state/*db-worker (:wrapped-worker client)) |
69 | | - (p/let [_ (state/<invoke-db-worker :thread-api/set-db-sync-config |
70 | | - (current-db-sync-config))] |
71 | | - nil) |
72 | | - (ldb/register-transact-fn! |
73 | | - (fn remote-transact! |
74 | | - [repo tx-data tx-meta] |
75 | | - (db-transact/transact browser/transact! |
76 | | - (if (string? repo) repo (state/get-current-repo)) |
77 | | - tx-data |
78 | | - (assoc tx-meta :client-id (:client-id @state/state))))) |
79 | | - client))) |
| 137 | + (let [session-id (str (random-uuid))] |
| 138 | + (p/let [_ (when @remote-db |
| 139 | + (remote/stop! @remote-db)) |
| 140 | + runtime (ipc/ipc "db-worker-runtime" repo) |
| 141 | + client (remote/start! (assoc runtime |
| 142 | + :repo repo |
| 143 | + :event-handler worker-handler/handle |
| 144 | + :on-invoke-success (fn [_method _args _result] |
| 145 | + (reset-active-request-failures! repo session-id)) |
| 146 | + :on-invoke-failure (fn [_method _args error] |
| 147 | + (record-active-request-failure! repo session-id error)) |
| 148 | + :on-event-error (fn [error] |
| 149 | + (record-active-request-failure! repo session-id error))))] |
| 150 | + (set-remote-runtime! repo client session-id) |
| 151 | + (p/let [_ (state/<invoke-db-worker :thread-api/set-db-sync-config |
| 152 | + (current-db-sync-config))] |
| 153 | + nil) |
| 154 | + (ldb/register-transact-fn! |
| 155 | + (fn remote-transact! |
| 156 | + [repo tx-data tx-meta] |
| 157 | + (db-transact/transact browser/transact! |
| 158 | + (if (string? repo) repo (state/get-current-repo)) |
| 159 | + tx-data |
| 160 | + (assoc tx-meta :client-id (:client-id @state/state))))) |
| 161 | + client)))) |
80 | 162 |
|
81 | 163 | (defn <start-runtime! |
82 | 164 | [] |
|
0 commit comments