Skip to content

Commit 5d5323f

Browse files
committed
perf(V2): resolve home org once per mutation guard invocation
assertOwnerOrgUidsAreHome queried the home org on every call, and the guard invoked it up to twice per record. For batch UPDATE paths (XLSX imports, cascade deletes) this issued thousands of identical home-org queries that always return the same row. Resolve the home org once per assertMutationOwnedByHomeOrg call and once per bulkCreate/staging-edit batch, then thread the resolved orgUid into assertOwnerOrgUidsAreHome instead of re-querying.
1 parent e16880c commit 5d5323f

1 file changed

Lines changed: 28 additions & 9 deletions

File tree

src/models/v2/staging-v2.model.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ class StagingV2 extends Model {
6464
}
6565

6666
static async bulkCreate(values, options) {
67+
// Resolve the home org once for the whole batch instead of per record.
68+
const needsGuard = Array.isArray(values) &&
69+
values.some((v) => ['UPDATE', 'DELETE'].includes(v?.action) && !v?.is_transfer);
70+
const homeOrgUid = needsGuard ? await StagingV2.resolveHomeOrgUid() : undefined;
6771
for (const value of values) {
68-
await StagingV2.assertMutationOwnedByHomeOrg(value, options);
72+
await StagingV2.assertMutationOwnedByHomeOrg(value, options, homeOrgUid);
6973
}
7074
StagingV2.changes.next(['staging']);
7175
const result = await super.bulkCreate(values, options);
@@ -121,14 +125,16 @@ class StagingV2 extends Model {
121125
transaction: options?.transaction,
122126
});
123127

128+
// Resolve the home org once for all matched staging records.
129+
const homeOrgUid = await StagingV2.resolveHomeOrgUid();
124130
for (const stagingRecord of stagingRecords) {
125131
await StagingV2.assertMutationOwnedByHomeOrg({
126132
uuid: values.uuid ?? stagingRecord.uuid,
127133
table: values.table ?? stagingRecord.table,
128134
action: values.action ?? stagingRecord.action,
129135
data: values.data ?? stagingRecord.data,
130136
is_transfer: values.is_transfer ?? stagingRecord.is_transfer,
131-
}, options);
137+
}, options, homeOrgUid);
132138
}
133139
}
134140

@@ -275,11 +281,20 @@ class StagingV2 extends Model {
275281
return [...new Set(ownerOrgUids)];
276282
}
277283

284+
static async resolveHomeOrgUid() {
285+
const homeOrg = await OrganizationsV2.findOne({
286+
where: { is_home: true },
287+
raw: true,
288+
});
289+
return homeOrg?.org_uid ?? null;
290+
}
291+
278292
static async assertOwnerOrgUidsAreHome(
279293
ownerOrgUids,
280294
table,
281295
requireOwner = false,
282296
unresolvedFields = [],
297+
homeOrgUid = null,
283298
) {
284299
if (unresolvedFields.length > 0) {
285300
throw new Error(
@@ -295,20 +310,16 @@ class StagingV2 extends Model {
295310
);
296311
}
297312

298-
const homeOrg = await OrganizationsV2.findOne({
299-
where: { is_home: true },
300-
raw: true,
301-
});
302-
const nonHomeOrgUid = ownerOrgUids.find((orgUid) => orgUid !== homeOrg?.org_uid);
313+
const nonHomeOrgUid = ownerOrgUids.find((orgUid) => orgUid !== homeOrgUid);
303314

304-
if (!homeOrg || nonHomeOrgUid) {
315+
if (!homeOrgUid || nonHomeOrgUid) {
305316
throw new Error(
306317
`Restricted data: cannot modify this ${table} record with orgUid '${nonHomeOrgUid}'. Only the home organization that created this record can modify it.`,
307318
);
308319
}
309320
}
310321

311-
static async assertMutationOwnedByHomeOrg(values, options) {
322+
static async assertMutationOwnedByHomeOrg(values, options, homeOrgUid) {
312323
if (!['UPDATE', 'DELETE'].includes(values?.action) || values?.is_transfer) {
313324
return;
314325
}
@@ -320,6 +331,12 @@ class StagingV2 extends Model {
320331
if (!primaryKeyField) return;
321332
const primaryKeyApiField = primaryKeyField.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
322333

334+
// Resolve the home org once per call (reused for every data row and both
335+
// ownership checks below) unless a batch caller already resolved it.
336+
if (homeOrgUid === undefined) {
337+
homeOrgUid = await StagingV2.resolveHomeOrgUid();
338+
}
339+
323340
const parsedData = Array.isArray(values.data)
324341
? values.data
325342
: JSON.parse(values.data || '[]');
@@ -349,6 +366,7 @@ class StagingV2 extends Model {
349366
values.table,
350367
hasOwnershipChain,
351368
existingUnresolved,
369+
homeOrgUid,
352370
);
353371
} else if (values.action !== 'UPDATE') {
354372
continue;
@@ -381,6 +399,7 @@ class StagingV2 extends Model {
381399
values.table,
382400
payloadHasOwnershipFields,
383401
unresolvedFields,
402+
homeOrgUid,
384403
);
385404
}
386405
}

0 commit comments

Comments
 (0)