fix: more lint error

This commit is contained in:
Tienson Qin
2026-01-26 19:47:28 +08:00
parent 94c6f698e4
commit 143a548f04
9 changed files with 180 additions and 164 deletions

View File

@@ -6,5 +6,6 @@
:consistent-alias :consistent-alias
{:aliases {clojure.string string}}} {:aliases {clojure.string string}}}
:lint-as {promesa.core/let clojure.core/let}
:skip-comments true :skip-comments true
:output {:progress true}} :output {:progress true}}

View File

@@ -41,22 +41,22 @@
(defn- get-jwks-keys (defn- get-jwks-keys
[url & {:keys [force?]}] [url & {:keys [force?]}]
(let [now (get-now-ms) (let [now (get-now-ms)
{:keys [url cached-url keys fetched-at]} {:cached-url (:url @*jwks-cache) {:keys [url cached-url jwks-keys fetched-at]} {:cached-url (:url @*jwks-cache)
:url url :url url
:keys (:keys @*jwks-cache) :jwks-keys (:keys @*jwks-cache)
:fetched-at (:fetched-at @*jwks-cache)} :fetched-at (:fetched-at @*jwks-cache)}
fresh? (and (not force?) fresh? (and (not force?)
(= cached-url url) (= cached-url url)
keys jwks-keys
(< (- now fetched-at) jwks-ttl-ms))] (< (- now fetched-at) jwks-ttl-ms))]
(if fresh? (if fresh?
(p/resolved keys) (p/resolved jwks-keys)
(p/let [jwks-resp (js/fetch url) (p/let [jwks-resp (js/fetch url)
_ (when-not (.-ok jwks-resp) (throw (ex-info "jwks" {}))) _ (when-not (.-ok jwks-resp) (throw (ex-info "jwks" {})))
jwks (.json jwks-resp) jwks (.json jwks-resp)
keys (or (aget jwks "keys") #js [])] jwks-keys (or (aget jwks "keys") #js [])]
(reset! *jwks-cache {:url url :keys keys :fetched-at now}) (reset! *jwks-cache {:url url :keys jwks-keys :fetched-at now})
keys)))) jwks-keys))))
(defn- base64url->uint8array [input] (defn- base64url->uint8array [input]
(let [pad (if (pos? (mod (count input) 4)) (let [pad (if (pos? (mod (count input) 4))
@@ -84,40 +84,40 @@
#js ["verify"])) #js ["verify"]))
(defn verify-jwt [token env] (defn verify-jwt [token env]
(let [parts (string/split token #"\.") (let [parts (string/split token #"\.")]
_ (when (not= 3 (count parts)) (throw (ex-info "invalid" {}))) (when (not= 3 (count parts)) (throw (ex-info "invalid" {})))
header-part (nth parts 0) (let [header-part (nth parts 0)
payload-part (nth parts 1) payload-part (nth parts 1)
signature-part (nth parts 2) signature-part (nth parts 2)
now-ms (get-now-ms) now-ms (get-now-ms)
now-s (js/Math.floor (/ now-ms 1000))] now-s (js/Math.floor (/ now-ms 1000))]
(if-let [cached (cached-token token now-s now-ms)] (if-let [cached (cached-token token now-s now-ms)]
(p/resolved cached) (p/resolved cached)
(p/let [header (decode-jwt-part header-part) (p/let [header (decode-jwt-part header-part)
payload (decode-jwt-part payload-part) payload (decode-jwt-part payload-part)
issuer (aget env "COGNITO_ISSUER") issuer (aget env "COGNITO_ISSUER")
client-id (aget env "COGNITO_CLIENT_ID") client-id (aget env "COGNITO_CLIENT_ID")
_ (when (not= (aget payload "iss") issuer) (throw (ex-info "iss not found" {}))) _ (when (not= (aget payload "iss") issuer) (throw (ex-info "iss not found" {})))
_ (when (not= (aget payload "aud") client-id) (throw (ex-info "aud not found" {}))) _ (when (not= (aget payload "aud") client-id) (throw (ex-info "aud not found" {})))
_ (when (and (aget payload "exp") (< (aget payload "exp") now-s)) _ (when (and (aget payload "exp") (< (aget payload "exp") now-s))
(throw (ex-info "exp" {}))) (throw (ex-info "exp" {})))
jwks-url (aget env "COGNITO_JWKS_URL") jwks-url (aget env "COGNITO_JWKS_URL")
keys (get-jwks-keys jwks-url) jwks-keys (get-jwks-keys jwks-url)
key (.find keys (fn [k] (= (aget k "kid") (aget header "kid")))) matching-key (.find jwks-keys (fn [k] (= (aget k "kid") (aget header "kid"))))
key (if key matching-key (if matching-key
key matching-key
(p/let [keys (get-jwks-keys jwks-url :force? true) (p/let [jwks-keys (get-jwks-keys jwks-url :force? true)
key (.find keys (fn [k] (= (aget k "kid") (aget header "kid"))))] matching-key (.find jwks-keys (fn [k] (= (aget k "kid") (aget header "kid"))))]
key)) matching-key))
_ (when-not key (throw (ex-info "kid" {}))) _ (when-not matching-key (throw (ex-info "kid" {})))
crypto-key (import-rsa-key key) crypto-key (import-rsa-key matching-key)
data (.encode text-encoder (str header-part "." payload-part)) data (.encode text-encoder (str header-part "." payload-part))
signature (base64url->uint8array signature-part) signature (base64url->uint8array signature-part)
ok (.verify js/crypto.subtle ok (.verify js/crypto.subtle
"RSASSA-PKCS1-v1_5" "RSASSA-PKCS1-v1_5"
crypto-key crypto-key
signature signature
data)] data)]
(when ok (when ok
(cache-token! token payload now-ms) (cache-token! token payload now-ms)
payload))))) payload))))))

18
deps/db-sync/.clj-condo/config.edn vendored Normal file
View File

@@ -0,0 +1,18 @@
{:linters
{:aliased-namespace-symbol {:level :warning}
:namespace-name-mismatch {:level :warning}
:used-underscored-binding {:level :warning}
:shadowed-var {:level :warning
:exclude [meta name key keys uuid type]}
:consistent-alias
{:aliases {clojure.pprint pprint
clojure.string string
datascript.core d
datascript.transit dt
logseq.publish.common publish-common
logseq.publish.model publish-model}}}
:lint-as {promesa.core/let clojure.core/let
shadow.cljs.modern/defclass clj-kondo.lint-as/def-catch-all}
:skip-comments true
:output {:progress true}}

View File

@@ -185,10 +185,6 @@
[^js self ^js ws] [^js self ^js ws]
(swap! (presence* self) dissoc ws)) (swap! (presence* self) dissoc ws))
(defn- fail-fast [tag data]
(log/error tag data)
(throw (ex-info (name tag) data)))
(defn- coerce-http-request [schema-key body] (defn- coerce-http-request [schema-key body]
(if-let [coercer (get db-sync-schema/http-request-coercers schema-key)] (if-let [coercer (get db-sync-schema/http-request-coercers schema-key)]
(let [coerced (coerce coercer body {:schema schema-key :dir :request})] (let [coerced (coerce coercer body {:schema schema-key :dir :request})]
@@ -277,11 +273,6 @@
(let [url (js/URL. (.-url request))] (let [url (js/URL. (.-url request))]
(str (.-origin url) "/assets/" graph-id "/" snapshot-id ".snapshot"))) (str (.-origin url) "/assets/" graph-id "/" snapshot-id ".snapshot")))
(defn- maybe-compress-stream [stream]
(if (exists? js/CompressionStream)
(.pipeThrough stream (js/CompressionStream. "gzip"))
stream))
(defn- maybe-decompress-stream [stream encoding] (defn- maybe-decompress-stream [stream encoding]
(if (and (= encoding snapshot-content-encoding) (exists? js/DecompressionStream)) (if (and (= encoding snapshot-content-encoding) (exists? js/DecompressionStream))
(.pipeThrough stream (js/DecompressionStream. "gzip")) (.pipeThrough stream (js/DecompressionStream. "gzip"))

View File

@@ -12,7 +12,7 @@
datascript.transit dt datascript.transit dt
logseq.publish.common publish-common logseq.publish.common publish-common
logseq.publish.model publish-model}}} logseq.publish.model publish-model}}}
:lint-as {logseq.publish.async/js-await clojure.core/let :lint-as {promesa.core/let clojure.core/let
shadow.cljs.modern/defclass clj-kondo.lint-as/def-catch-all} shadow.cljs.modern/defclass clj-kondo.lint-as/def-catch-all}
:skip-comments true :skip-comments true
:output {:progress true}} :output {:progress true}}

View File

@@ -110,18 +110,6 @@ $ typos -w
To configure it e.g. for dealing with false positives, see `typos.toml`. To configure it e.g. for dealing with false positives, see `typos.toml`.
### Separate DB and File Graph Code
There is a growing number of code and features that are only for file or DB graphs. Run this linter to
ensure that code you add or modify keeps with existing conventions:
```
$ bb lint:db-and-file-graphs-separate
✅ All checks passed!
```
The main convention is that file and db specific files go under directories named `file_based` and `db_based` respectively. To see the full list of file and db specific namespaces and files see the top of [the script](/scripts/src/logseq/tasks/dev/db_and_file_graphs.clj).
### Separate Worker from Frontend ### Separate Worker from Frontend
The worker and frontend code share common code from deps/ and `frontend.common.*`. However, the worker should never depend on other frontend namespaces as it could pull in libraries like React which cause it to fail hard. Likewise the frontend should never depend on worker namespaces. Run this linter to ensure worker and frontend namespaces don't require each other: The worker and frontend code share common code from deps/ and `frontend.common.*`. However, the worker should never depend on other frontend namespaces as it could pull in libraries like React which cause it to fail hard. Likewise the frontend should never depend on worker namespaces. Run this linter to ensure worker and frontend namespaces don't require each other:
@@ -363,7 +351,7 @@ These tasks are specific to database graphs. For these tasks there is a one time
$ bb dev:transact test-db '[:find ?b :where [?b :block/title "say wut"]]' '(fn [id] (vector :db/add id :block/title "say woot!"))' $ bb dev:transact test-db '[:find ?b :where [?b :block/title "say wut"]]' '(fn [id] (vector :db/add id :block/title "say woot!"))'
Updated 1 block(s) for graph test-db! Updated 1 block(s) for graph test-db!
``` ```
Run the dev command `Replace graph with its db.sqlite file` to use the updated graph in the desktop app. Run the dev command `Replace graph with its db.sqlite file` to use the updated graph in the desktop app.
* `dev:create` - Create a DB graph given a `sqlite.build` EDN file * `dev:create` - Create a DB graph given a `sqlite.build` EDN file

View File

@@ -15,7 +15,6 @@
"bb lint:carve" "bb lint:carve"
"bb lint:large-vars" "bb lint:large-vars"
"bb lint:worker-and-frontend-separate" "bb lint:worker-and-frontend-separate"
"bb lint:db-and-file-graphs-separate"
"bb lang:validate-translations" "bb lang:validate-translations"
"bb lint:ns-docstrings"]] "bb lint:ns-docstrings"]]
(println cmd) (println cmd)

View File

@@ -1080,6 +1080,61 @@
(concat (map (fn [id] [:db/retractEntity id]) retract-block-ids)) (concat (map (fn [id] [:db/retractEntity id]) retract-block-ids))
keep-last-update))) keep-last-update)))
(defn- apply-remote-tx-with-local-changes!
[{:keys [conn local-txs reversed-tx-data safe-remote-tx-data remote-deleted-blocks
temp-tx-meta *remote-tx-report *reversed-tx-report *remote-deleted-ids *rebase-tx-data]}]
(let [batch-tx-meta {:rtc-tx? true
;; Reverse/rebase batches can temporarily violate block schema.
:skip-validate-db? true}]
(ldb/transact-with-temp-conn!
conn
batch-tx-meta
(fn [temp-conn _*batch-tx-data]
(let [tx-meta temp-tx-meta
reversed-tx-report (ldb/transact! temp-conn reversed-tx-data (assoc tx-meta :op :reverse))
_ (reset! *reversed-tx-report reversed-tx-report)
;; 2. transact remote tx-data
remote-tx-report (let [tx-meta (assoc tx-meta :op :transact-remote-tx-data)]
(ldb/transact! temp-conn safe-remote-tx-data tx-meta))
_ (reset! *remote-tx-report remote-tx-report)
local-deleted-blocks (get-local-deleted-blocks reversed-tx-report reversed-tx-data)
_ (when (seq remote-deleted-blocks)
(reset! *remote-deleted-ids (set (map :block/uuid remote-deleted-blocks))))
deleted-nodes (concat local-deleted-blocks remote-deleted-blocks)
deleted-ids (set (keep :block/uuid deleted-nodes))
;; 3. rebase pending local txs
rebase-tx-report (when (seq local-txs)
(let [pending-tx-data (mapcat :tx local-txs)
rebased-tx-data (sanitize-tx-data
(or (:db-after remote-tx-report)
(:db-after reversed-tx-report))
pending-tx-data
(set (map :block/uuid local-deleted-blocks)))]
(when (seq rebased-tx-data)
(ldb/transact! temp-conn rebased-tx-data (assoc tx-meta :op :rebase)))))
;; 4. delete nodes and fix tx data
db @temp-conn
deleted-nodes (keep (fn [id] (d/entity db [:block/uuid id])) deleted-ids)]
(delete-nodes! temp-conn deleted-nodes (assoc tx-meta :op :delete-blocks))
(fix-tx! temp-conn remote-tx-report rebase-tx-report (assoc tx-meta :op :fix))))
{:listen-db (fn [{:keys [tx-meta tx-data]}]
(when-not (contains? #{:reverse :transact-remote-tx-data} (:op tx-meta))
(swap! *rebase-tx-data into tx-data)))})))
(defn- apply-remote-tx-without-local-changes!
[{:keys [conn safe-remote-tx-data remote-deleted-block-ids temp-tx-meta]}]
(let [db @conn]
(ldb/transact-with-temp-conn!
conn
{:rtc-tx? true}
(fn [temp-conn]
(when (seq safe-remote-tx-data)
(d/transact! temp-conn safe-remote-tx-data {:rtc-tx? true}))
(when-let [deleted-nodes (keep (fn [id] (d/entity db [:block/uuid id]))
remote-deleted-block-ids)]
(delete-nodes! temp-conn deleted-nodes
(assoc temp-tx-meta :op :delete-blocks)))))))
(defn- apply-remote-tx! (defn- apply-remote-tx!
[repo client tx-data*] [repo client tx-data*]
(if-let [conn (worker-state/get-datascript-conn repo)] (if-let [conn (worker-state/get-datascript-conn repo)]
@@ -1108,57 +1163,20 @@
:temp-conn? true :temp-conn? true
:gen-undo-ops? false :gen-undo-ops? false
:persist-op? false} :persist-op? false}
tx-report apply-context {:conn conn
(if has-local-changes? :local-txs local-txs
(let [batch-tx-meta {:rtc-tx? true}] :reversed-tx-data reversed-tx-data
(ldb/transact-with-temp-conn! :safe-remote-tx-data safe-remote-tx-data
conn :remote-deleted-blocks remote-deleted-blocks
batch-tx-meta :remote-deleted-block-ids remote-deleted-block-ids
(fn [temp-conn _*batch-tx-data] :temp-tx-meta temp-tx-meta
(let [tx-meta temp-tx-meta :*remote-tx-report *remote-tx-report
reversed-tx-report (ldb/transact! temp-conn reversed-tx-data (assoc tx-meta :op :reverse)) :*reversed-tx-report *reversed-tx-report
_ (reset! *reversed-tx-report reversed-tx-report) :*remote-deleted-ids *remote-deleted-ids
;; 2. transact remote tx-data :*rebase-tx-data *rebase-tx-data}
remote-tx-report (let [tx-meta (assoc tx-meta :op :transact-remote-tx-data)] tx-report (if has-local-changes?
(ldb/transact! temp-conn safe-remote-tx-data tx-meta)) (apply-remote-tx-with-local-changes! apply-context)
_ (reset! *remote-tx-report remote-tx-report) (apply-remote-tx-without-local-changes! apply-context))
local-deleted-blocks (get-local-deleted-blocks reversed-tx-report reversed-tx-data)
_ (when (seq remote-deleted-blocks)
(reset! *remote-deleted-ids (set (map :block/uuid remote-deleted-blocks))))
;; _ (prn :debug
;; :local-deleted-blocks (map (fn [b] (select-keys b [:db/id :block/title])) local-deleted-blocks)
;; :remote-deleted-blocks remote-deleted-blocks)
deleted-nodes (concat local-deleted-blocks remote-deleted-blocks)
deleted-ids (set (keep :block/uuid deleted-nodes))
;; 3. rebase pending local txs
rebase-tx-report (when (seq local-txs)
(let [pending-tx-data (mapcat :tx local-txs)
rebased-tx-data (sanitize-tx-data
(or (:db-after remote-tx-report)
(:db-after reversed-tx-report))
pending-tx-data
(set (map :block/uuid local-deleted-blocks)))]
;; (prn :debug :pending-tx-data pending-tx-data)
;; (prn :debug :rebased-tx-data rebased-tx-data)
(when (seq rebased-tx-data)
(ldb/transact! temp-conn rebased-tx-data (assoc tx-meta :op :rebase)))))
;; 4. delete nodes and fix tx data
db @temp-conn
deleted-nodes (keep (fn [id] (d/entity db [:block/uuid id])) deleted-ids)]
(delete-nodes! temp-conn deleted-nodes (assoc tx-meta :op :delete-blocks))
(fix-tx! temp-conn remote-tx-report rebase-tx-report (assoc tx-meta :op :fix))))
{:listen-db (fn [{:keys [tx-meta tx-data]}]
(when-not (contains? #{:reverse :transact-remote-tx-data} (:op tx-meta))
(swap! *rebase-tx-data into tx-data)))}))
(ldb/transact-with-temp-conn!
conn
{:rtc-tx? true}
(fn [temp-conn]
(when (seq safe-remote-tx-data)
(d/transact! temp-conn safe-remote-tx-data {:rtc-tx? true}))
(when-let [deleted-nodes (keep (fn [id] (d/entity db [:block/uuid id])) remote-deleted-block-ids)]
(delete-nodes! temp-conn deleted-nodes
(assoc temp-tx-meta :op :delete-blocks))))))
remote-tx-report @*remote-tx-report] remote-tx-report @*remote-tx-report]
;; persist rebase tx to client ops ;; persist rebase tx to client ops
(when has-local-changes? (when has-local-changes?

View File

@@ -423,6 +423,38 @@
(str "db empty seed=" seed " history=" (count @history)))))))))) (str "db empty seed=" seed " history=" (count @history))))))))))
(defonce op-runs 500) (defonce op-runs 500)
(defn- run-random-ops!
[rng server clients repo->state base-uuid history run-ops-opts steps]
(dotimes [_ steps]
(let [client (rand-nth! rng clients)
state (get repo->state (:repo client))]
(run-ops! rng (assoc client :base-uuid base-uuid :state state) 1 history run-ops-opts)
(sync-loop! server clients))))
(defn- run-local-ops!
[rng conn base-uuid state history run-ops-opts steps]
(dotimes [_ steps]
(run-ops! rng {:conn conn :base-uuid base-uuid :state state} 1 history run-ops-opts)))
(defn- assert-synced-attrs!
[seed history attrs-a attrs-b attrs-c]
(when-not (= attrs-a attrs-b)
(let [[a b] (take 2 (data/diff attrs-a attrs-b))]
(prn :debug :diff :attrs-a a :attrs-b b)))
(when-not (= attrs-a attrs-c)
(let [[a c] (take 2 (data/diff attrs-a attrs-c))]
(prn :debug :diff :attrs-a a :attrs-c c)))
(is (= attrs-a attrs-b)
(str "db mismatch A/B seed=" seed
" a=" (count attrs-a)
" b=" (count attrs-b)
" history=" (count @history)))
(is (= attrs-a attrs-c)
(str "db mismatch A/C seed=" seed
" a=" (count attrs-a)
" c=" (count attrs-c)
" history=" (count @history))))
(deftest three-clients-single-repo-sim-test (deftest three-clients-single-repo-sim-test
(prn :debug "run three-clients-single-repo-sim-test") (prn :debug "run three-clients-single-repo-sim-test")
(testing "db-sync convergence with three clients sharing one repo" (testing "db-sync convergence with three clients sharing one repo"
@@ -451,12 +483,10 @@
repo-c {:conn conn-c :ops-conn ops-c}} repo-c {:conn conn-c :ops-conn ops-c}}
(fn [] (fn []
(reset! db-sync/*repo->latest-remote-tx {}) (reset! db-sync/*repo->latest-remote-tx {})
(ensure-base-page! conn-a base-uuid) (doseq [conn [conn-a conn-b conn-c]]
(ensure-base-page! conn-b base-uuid) (ensure-base-page! conn base-uuid))
(ensure-base-page! conn-c base-uuid) (doseq [repo [repo-a repo-b repo-c]]
(client-op/update-local-tx repo-a 0) (client-op/update-local-tx repo 0))
(client-op/update-local-tx repo-b 0)
(client-op/update-local-tx repo-c 0)
(let [clients [{:repo repo-a :conn conn-a :client client-a :online? true} (let [clients [{:repo repo-a :conn conn-a :client client-a :online? true}
{:repo repo-b :conn conn-b :client client-b :online? true} {:repo repo-b :conn conn-b :client client-b :online? true}
{:repo repo-c :conn conn-c :client client-c :online? true}] {:repo repo-c :conn conn-c :client client-c :online? true}]
@@ -464,24 +494,16 @@
run-ops-opts {}] run-ops-opts {}]
(prn :debug :phase-a) (prn :debug :phase-a)
;; Phase A: all online ;; Phase A: all online
(dotimes [_ op-runs] (run-random-ops! rng server clients repo->state base-uuid history run-ops-opts op-runs)
(let [client (rand-nth! rng clients)
state (get repo->state (:repo client))]
(run-ops! rng (assoc client :base-uuid base-uuid :state state) 1 history run-ops-opts)
(sync-loop! server clients)))
;; Phase B: C offline, A/B online ;; Phase B: C offline, A/B online
(prn :debug :phase-b-c-offline) (prn :debug :phase-b-c-offline)
(let [clients-phase-b [{:repo repo-a :conn conn-a :client client-a :online? true} (let [clients-phase-b [{:repo repo-a :conn conn-a :client client-a :online? true}
{:repo repo-b :conn conn-b :client client-b :online? true} {:repo repo-b :conn conn-b :client client-b :online? true}
{:repo repo-c :conn conn-c :client client-c :online? false}]] {:repo repo-c :conn conn-c :client client-c :online? false}]]
(dotimes [_ op-runs] (run-random-ops! rng server (subvec (vec clients-phase-b) 0 2) repo->state
(let [client (rand-nth! rng (subvec (vec clients-phase-b) 0 2)) base-uuid history run-ops-opts op-runs)
state (get repo->state (:repo client))] (run-local-ops! rng conn-c base-uuid state-c history run-ops-opts op-runs))
(run-ops! rng (assoc client :base-uuid base-uuid :state state) 1 history run-ops-opts)
(sync-loop! server clients-phase-b)))
(dotimes [_ op-runs]
(run-ops! rng {:client client-c :conn conn-c :base-uuid base-uuid :state state-c} 1 history run-ops-opts)))
;; Phase C: reconnect C ;; Phase C: reconnect C
(prn :debug :phase-c-reconnect) (prn :debug :phase-c-reconnect)
@@ -492,13 +514,9 @@
(let [clients-phase-d [{:repo repo-a :conn conn-a :client client-a :online? false} (let [clients-phase-d [{:repo repo-a :conn conn-a :client client-a :online? false}
{:repo repo-b :conn conn-b :client client-b :online? true} {:repo repo-b :conn conn-b :client client-b :online? true}
{:repo repo-c :conn conn-c :client client-c :online? true}]] {:repo repo-c :conn conn-c :client client-c :online? true}]]
(dotimes [_ op-runs] (run-random-ops! rng server (subvec (vec clients-phase-d) 1 3) repo->state
(let [client (rand-nth! rng (subvec (vec clients-phase-d) 1 3)) base-uuid history run-ops-opts op-runs)
state (get repo->state (:repo client))] (run-local-ops! rng conn-a base-uuid state-a history run-ops-opts op-runs))
(run-ops! rng (assoc client :base-uuid base-uuid :state state) 1 history run-ops-opts)
(sync-loop! server clients-phase-d)))
(dotimes [_ op-runs]
(run-ops! rng {:conn conn-a :base-uuid base-uuid :state state-a} 1 history run-ops-opts)))
;; Final sync ;; Final sync
(prn :debug :final-sync) (prn :debug :final-sync)
@@ -514,21 +532,4 @@
(let [attrs-a (block-attr-map @conn-a) (let [attrs-a (block-attr-map @conn-a)
attrs-b (block-attr-map @conn-b) attrs-b (block-attr-map @conn-b)
attrs-c (block-attr-map @conn-c)] attrs-c (block-attr-map @conn-c)]
(when-not (= attrs-a attrs-b) (assert-synced-attrs! seed history attrs-a attrs-b attrs-c))))))))
(let [[a b] (take 2 (data/diff attrs-a attrs-b))]
(prn :debug :diff :attrs-a a
:attrs-b b)))
(when-not (= attrs-a attrs-c)
(let [[a c] (take 2 (data/diff attrs-a attrs-c))]
(prn :debug :diff :attrs-a a
:attrs-c c)))
(is (= attrs-a attrs-b)
(str "db mismatch A/B seed=" seed
" a=" (count attrs-a)
" b=" (count attrs-b)
" history=" (count @history)))
(is (= attrs-a attrs-c)
(str "db mismatch A/C seed=" seed
" a=" (count attrs-a)
" c=" (count attrs-c)
" history=" (count @history))))))))))