Andrey Antukh 67643340a2 ✨ Partition file_change by file_id
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
2026-11-05 22:40:17 +01:00

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)))))