Skip to content

Commit 05f4b7a

Browse files
authored
Merge pull request #1841 from jetstreamapp/bug/max-query-offset
fix: reduce number of objects included in query
2 parents b13592f + e31c7c1 commit 05f4b7a

2 files changed

Lines changed: 23 additions & 10 deletions

File tree

libs/features/manage-permissions/src/usePermissionRecords.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { logger } from '@jetstream/shared/client-logger';
22
import { describeSObject, queryAll, queryAllUsingOffset } from '@jetstream/shared/data';
33
import { tracker } from '@jetstream/shared/ui-utils';
4-
import { getErrorMessage, groupByFlat } from '@jetstream/shared/utils';
4+
import { getErrorMessage, groupByFlat, splitArrayToMaxSize } from '@jetstream/shared/utils';
55
import {
66
EntityParticlePermissionsRecord,
77
FieldPermissionDefinitionMap,
@@ -133,21 +133,28 @@ export function usePermissionRecords(selectedOrg: SalesforceOrgUi, sobjects: str
133133
};
134134
}
135135

136+
/**
137+
* Number of queries to run concurrently. Offset-paged queries (EntityParticle) are split into many small batches to stay
138+
* under Salesforce's OFFSET cap, so we run them in bounded waves rather than sequentially to keep large selections fast.
139+
*/
140+
const QUERY_CONCURRENCY = 5;
141+
136142
// This could be eligible to pull into generic method for expanded use
137143
async function queryAndCombineResults<T>(
138144
selectedOrg: SalesforceOrgUi,
139145
queries: string[],
140146
useOffset = false,
141147
isTooling = false,
142148
): Promise<T[]> {
143-
let output: T[] = [];
144-
for (const currQuery of queries) {
145-
if (useOffset) {
146-
const { queryResults } = await queryAllUsingOffset<T>(selectedOrg, currQuery, isTooling);
147-
output = output.concat(queryResults.records);
148-
} else {
149-
const { queryResults } = await queryAll<T>(selectedOrg, currQuery, isTooling);
150-
output = output.concat(queryResults.records);
149+
const runQuery = (currQuery: string) =>
150+
useOffset ? queryAllUsingOffset<T>(selectedOrg, currQuery, isTooling) : queryAll<T>(selectedOrg, currQuery, isTooling);
151+
152+
const output: T[] = [];
153+
// Results are keyed/grouped downstream, so ordering does not matter - run each wave concurrently
154+
for (const wave of splitArrayToMaxSize(queries, QUERY_CONCURRENCY)) {
155+
const waveResults = await Promise.all(wave.map(runQuery));
156+
for (const { queryResults } of waveResults) {
157+
output.push(...queryResults.records);
151158
}
152159
}
153160
return output;

libs/features/manage-permissions/src/utils/permission-manager-utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ import { Query, WhereClause, composeQuery, getField } from '@jetstreamapp/soql-p
3232

3333
const MAX_OBJ_IN_QUERY = 100;
3434

35+
/**
36+
* EntityParticle does not support queryMore, so we page it using OFFSET, which Salesforce caps at 2000 for this object.
37+
* Keep the objects really small to avoid hitting the 2000 limit
38+
*/
39+
const MAX_OBJ_IN_PERMISSIONABLE_FIELDS_QUERY = 2;
40+
3541
export function filterPermissionsSobjects(sobject: DescribeGlobalSObjectResult | null) {
3642
if (!sobject) {
3743
return false;
@@ -569,7 +575,7 @@ export function permissionsHaveError<T extends PermissionDefinitionMap>(permissi
569575
* @returns query for all permissionable fields
570576
*/
571577
export function getQueryForAllPermissionableFields(allSobjects: string[]): string[] {
572-
const queries = splitArrayToMaxSize(allSobjects, MAX_OBJ_IN_QUERY).map((sobjects) => {
578+
const queries = splitArrayToMaxSize(allSobjects, MAX_OBJ_IN_PERMISSIONABLE_FIELDS_QUERY).map((sobjects) => {
573579
return composeQuery({
574580
fields: [
575581
getField('QualifiedApiName'),

0 commit comments

Comments
 (0)