mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(documents): serialise the upload quota gate per user
Concurrent uploads all read the same pre-insert usage SUM, so a burst could overshoot the per-user quota by rate-limit x cap in one window. A transaction-scoped advisory lock keyed on the user id serialises the sum-and-insert; uploads by different users never queue on each other. Integration probes pin both races: four parallel uploads against a two-file quota land exactly two, and two racing identical uploads collapse onto one row via the partial unique index. Also aligns the round-trip integration test with the serve-route CSP posture: the proxy carve-out owns the response CSP, the route sets none.
This commit is contained in:
1 parent
23e371965b
commit
6bab2ce774
2 files changed
+70
-1
No files matched your search
@@ -261,6 +261,18 @@ async function postUpload(request: Request): Promise<Response> {
|
||||
let document: SerialisableDocument;
|
||||
try {
|
||||
document = await prisma.$transaction(async (tx) => {
|
||||
// Serialise the quota gate per user: without this, N concurrent
|
||||
// uploads all read the same SUM before any of them commits and the
|
||||
// quota can be overshot by up to N × cap in one burst. The advisory
|
||||
// lock is transaction-scoped (released on commit/rollback) and keyed
|
||||
// on the user id, so uploads by different users never queue on each
|
||||
// other.
|
||||
// (`pg_advisory_xact_lock` returns void, which the client cannot
|
||||
// deserialize as a column — selecting FROM it yields a plain int row.)
|
||||
await tx.$queryRaw`
|
||||
SELECT 1 AS locked
|
||||
FROM pg_advisory_xact_lock(hashtextextended('documents-quota:' || ${user.id}, 0))
|
||||
`;
|
||||
const rows = await tx.$queryRaw<Array<{ used: bigint }>>`
|
||||
SELECT COALESCE(SUM(byte_size), 0)::bigint AS used
|
||||
FROM inbound_documents
|
||||
|
||||
Reference in new issue
Block a user