mirror of
https://github.com/penpot/penpot.git
synced 2026-11-08 11:04:44 +00:00
Partition `file_change` by `HASH (file_id)` in 16 partitions, with the same bounds as `file_data`, so a join between them is resolved partition by partition. The primary key becomes `(file_id, id)`, which a partitioned table needs, and every access filters by `file_id`. The new table carries a `kind` column (1 snapshot, 2 revision) as a reserve: the queries still tell them apart by the label, and `kind` takes over when they are rewritten. The payload of a snapshot stays in `file_data`; the `data` column is kept for reading old `legacy-db` payloads. The four snapshot commands require `file-id`, because it prunes the partition. The objects GC locks and deletes by the pair, in batches. The old table is kept as `file_change_legacy` as the way back. The automatic migration refuses to run above 10 GiB or 2M rows and points to the runbook; big instances partition online with that runbook, which copies by id, follows the `updated_at` watermark in delta passes and checks that no row of the source is missing from the target. Closes #12416 AI-assisted-by: deepseek-v4.1-flash
91 lines
3.5 KiB
Clojure
91 lines
3.5 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
|
|
|
(ns backend-tests.db-test
|
|
(:require
|
|
[app.common.uuid :as uuid]
|
|
[app.db :as db]
|
|
[backend-tests.helpers :as th]
|
|
[clojure.test :as t]
|
|
[integrant.core :as ig])
|
|
(:import
|
|
com.zaxxer.hikari.HikariConfig
|
|
com.zaxxer.hikari.HikariDataSource
|
|
java.sql.Connection))
|
|
|
|
(t/use-fixtures :once th/state-init)
|
|
|
|
(defn- get-consistent-pool-stats
|
|
"Read pool stats until total equals active + idle, or attempts run out.
|
|
Hikari exposes each counter through a separate MXBean call, so a borrow
|
|
or return between the reads can leave a single snapshot inconsistent
|
|
(total off by one). Retrying until the pool is quiescent keeps the test
|
|
deterministic without weakening the invariant."
|
|
[pool]
|
|
(loop [attempt 20]
|
|
(let [stats (db/pool-stats pool)
|
|
total (:total-connections stats)
|
|
active (:active-connections stats)
|
|
idle (:idle-connections stats)]
|
|
(if (or (= total (+ active idle))
|
|
(zero? attempt))
|
|
stats
|
|
(do (Thread/sleep 10)
|
|
(recur (dec attempt)))))))
|
|
|
|
(t/deftest pool-requires-metrics
|
|
;; Metrics is required by the pool schema: a pool wired without it
|
|
;; fails at the boundary (AssertionError when asserts are enabled) or
|
|
;; when the prometheus tracker is wired, instead of silently skipping
|
|
;; the instrumentation.
|
|
(t/is (thrown? Throwable
|
|
(ig/init-key :app.db/pool
|
|
{::db/uri (java.net.URI. "postgresql://localhost/test")}))))
|
|
|
|
(t/deftest pool-stats-returns-expected-keys
|
|
(let [stats (get-consistent-pool-stats th/*pool*)]
|
|
(t/testing "all expected keys are present"
|
|
(t/is (contains? stats :active-connections))
|
|
(t/is (contains? stats :idle-connections))
|
|
(t/is (contains? stats :threads-awaiting-connection))
|
|
(t/is (contains? stats :total-connections))
|
|
(t/is (contains? stats :maximum-pool-size))
|
|
(t/is (contains? stats :minimum-idle)))
|
|
|
|
(t/testing "values are non-negative integers"
|
|
(t/is (>= (:active-connections stats) 0))
|
|
(t/is (>= (:idle-connections stats) 0))
|
|
(t/is (>= (:threads-awaiting-connection stats) 0))
|
|
(t/is (>= (:total-connections stats) 0))
|
|
(t/is (>= (:maximum-pool-size stats) 0))
|
|
(t/is (>= (:minimum-idle stats) 0)))
|
|
|
|
(t/testing "total connections equals active + idle"
|
|
(t/is (= (:total-connections stats)
|
|
(+ (:active-connections stats)
|
|
(:idle-connections stats)))))
|
|
|
|
(t/testing "maximum pool size is reasonable"
|
|
(t/is (pos? (:maximum-pool-size stats))))))
|
|
|
|
(t/deftest uuid->hash-code-is-deterministic
|
|
(t/is (= (db/uuid->hash-code uuid/zero)
|
|
(db/uuid->hash-code uuid/zero))))
|
|
|
|
(t/deftest uuid->hash-code-returns-long
|
|
(t/is (instance? Long (db/uuid->hash-code uuid/zero))))
|
|
|
|
(t/deftest uuid->hash-code-stable-for-zero-uuid
|
|
(t/is (= 3659997967308761462 (db/uuid->hash-code uuid/zero))))
|
|
|
|
(t/deftest pool-enables-the-partitionwise-join
|
|
;; The pool sets the GUC on every connection, so the planner can resolve a
|
|
;; join between two partitioned tables pairwise, partition by partition.
|
|
(let [row (db/exec-one! th/*pool*
|
|
["SELECT current_setting('enable_partitionwise_join') AS partitionwise_join"])]
|
|
|
|
(t/is (= "on" (:partitionwise-join row)))))
|