penpot/backend/test/backend_tests/fdata_resolve_test.clj
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

43 lines
1.8 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 INC Sucursal en España SL
(ns backend-tests.fdata-resolve-test
"Resolution of file data by backend (`app.features.fdata/resolve-file-data`).
A row without a `file_data` twin carries no backend: the read queries mark
it with `coalesce(fd.backend, 'legacy-db')` and hand its blob over as
`:legacy-data`. The `legacy-db` backend is still supported, so that case
has to resolve to the blob instead of nil."
(:require
[app.features.fdata :as fdata]
[clojure.test :as t]))
(t/deftest legacy-db-resolves-the-legacy-blob
(let [blob (vec (.getBytes "legacy-blob" "UTF-8"))
file {:backend "legacy-db"
:legacy-data (byte-array blob)}
out (fdata/resolve-file-data nil file)]
(t/is (= blob (vec (:data out))))
(t/is (not (contains? out :legacy-data)))))
(t/deftest missing-backend-defaults-to-legacy-db
;; The queries leave `:backend` nil when the `LEFT JOIN` with `file_data`
;; finds no twin; the dispatch default keeps that row on `legacy-db`.
(let [blob (vec (.getBytes "legacy-blob" "UTF-8"))
file {:legacy-data (byte-array blob)}
out (fdata/resolve-file-data nil file)]
(t/is (= blob (vec (:data out))))
(t/is (not (contains? out :legacy-data)))))
(t/deftest db-backend-drops-the-legacy-key
(let [data (vec (.getBytes "db-blob" "UTF-8"))
file {:backend "db"
:data (byte-array data)
:legacy-data (byte-array (vec (.getBytes "stale" "UTF-8")))}
out (fdata/resolve-file-data nil file)]
(t/is (= data (vec (:data out))))
(t/is (not (contains? out :legacy-data)))))