mirror of
https://github.com/penpot/penpot.git
synced 2026-08-18 18:58:40 +00:00
🐛 Fix deduplicated storage overcounting in media-storage-bytes-per-team quote
The SQL query sql:get-media-storage-bytes-per-team used UNION ALL across six SELECT branches that each produce a so_id reference. When deduplication causes multiple file_media_object or team_font_variant rows to point at the same storage_object, UNION ALL counts that objects size once per reference — inflating "used bytes" and causing false :max-quote-reached rejections. Change all five UNION ALL to UNION so that duplicate so_id values are collapsed before the JOIN storage_object / SUM(so.size). Add a test (media-storage-bytes-quote-deduped) that creates one storage_object referenced by two file_media_object rows and asserts the computed usage reflects the deduplicated physical size, not 2x. AI-assisted-by: mimo-v2.5
This commit is contained in:
parent
049205d0fc
commit
6d481eb15b
@ -568,8 +568,8 @@
|
||||
WHERE p.team_id = ?
|
||||
AND fmo.deleted_at IS NULL
|
||||
AND f.deleted_at IS NULL
|
||||
UNION ALL
|
||||
SELECT fmo.thumbnail_id AS so_id
|
||||
UNION
|
||||
SELECT fmo.thumbnail_id AS so_id
|
||||
FROM file_media_object AS fmo
|
||||
JOIN file AS f ON (f.id = fmo.file_id)
|
||||
JOIN project AS p ON (p.id = f.project_id)
|
||||
@ -577,26 +577,26 @@
|
||||
AND fmo.thumbnail_id IS NOT NULL
|
||||
AND fmo.deleted_at IS NULL
|
||||
AND f.deleted_at IS NULL
|
||||
UNION ALL
|
||||
SELECT v.otf_file_id AS so_id
|
||||
UNION
|
||||
SELECT v.otf_file_id AS so_id
|
||||
FROM team_font_variant AS v
|
||||
WHERE v.team_id = ?
|
||||
AND v.otf_file_id IS NOT NULL
|
||||
AND v.deleted_at IS NULL
|
||||
UNION ALL
|
||||
SELECT v.ttf_file_id AS so_id
|
||||
UNION
|
||||
SELECT v.ttf_file_id AS so_id
|
||||
FROM team_font_variant AS v
|
||||
WHERE v.team_id = ?
|
||||
AND v.ttf_file_id IS NOT NULL
|
||||
AND v.deleted_at IS NULL
|
||||
UNION ALL
|
||||
SELECT v.woff1_file_id AS so_id
|
||||
UNION
|
||||
SELECT v.woff1_file_id AS so_id
|
||||
FROM team_font_variant AS v
|
||||
WHERE v.team_id = ?
|
||||
AND v.woff1_file_id IS NOT NULL
|
||||
AND v.deleted_at IS NULL
|
||||
UNION ALL
|
||||
SELECT v.woff2_file_id AS so_id
|
||||
UNION
|
||||
SELECT v.woff2_file_id AS so_id
|
||||
FROM team_font_variant AS v
|
||||
WHERE v.team_id = ?
|
||||
AND v.woff2_file_id IS NOT NULL
|
||||
|
||||
@ -400,6 +400,47 @@
|
||||
;; total=0, incr=500, best quote=1000 → 0+500 <= 1000 → ok
|
||||
(check-ok! "allowed by team+profile quote"))))
|
||||
|
||||
(t/deftest media-storage-bytes-quote-deduped
|
||||
(with-mocks [mock {:target 'app.config/get
|
||||
:return (th/config-get-mock
|
||||
{:quotes-media-storage-bytes-per-team 1100})}]
|
||||
|
||||
(let [prof (th/create-profile* 1)
|
||||
team-id (:default-team-id prof)
|
||||
proj (th/create-project* 1 {:profile-id (:id prof)
|
||||
:team-id team-id})
|
||||
file1 (th/create-file* 1 {:profile-id (:id prof)
|
||||
:project-id (:id proj)
|
||||
:is-shared false})
|
||||
file2 (th/create-file* 2 {:profile-id (:id prof)
|
||||
:project-id (:id proj)
|
||||
:is-shared false})
|
||||
|
||||
;; One physical storage object of 500 bytes
|
||||
so-id (uuid/random)
|
||||
_ (th/db-insert! :storage-object {:id so-id
|
||||
:size 500
|
||||
:backend "test"})
|
||||
|
||||
;; Two file_media_object rows pointing at the SAME storage object
|
||||
;; (simulates the deduplication path: same content uploaded twice)
|
||||
_ (th/create-file-media-object*
|
||||
{:file-id (:id file1) :media-id so-id
|
||||
:name "icon" :mtype "image/svg+xml"})
|
||||
_ (th/create-file-media-object*
|
||||
{:file-id (:id file2) :media-id so-id
|
||||
:name "icon" :mtype "image/svg+xml"})
|
||||
|
||||
data {::quotes/id ::quotes/media-storage-bytes-per-team
|
||||
::quotes/profile-id (:id prof)
|
||||
::quotes/team-id team-id
|
||||
::quotes/incr 200}]
|
||||
|
||||
;; Physical size is 500. With UNION (correct), total=500, 500+200=700 ≤ 1100 → ok.
|
||||
;; With UNION ALL (buggy), total=1000, 1000+200=1200 > 1100 → rejected.
|
||||
(quotes/check! th/*system* data)
|
||||
(t/is (true? true) "deduped storage counted once, under quota"))))
|
||||
|
||||
(t/deftest media-upload-enforces-storage-quote
|
||||
(with-mocks [mock {:target 'app.config/get
|
||||
:return (th/config-get-mock
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user