Prevent querying too much on worklow index page (#14916)

## Performance short term fix

Done in this PR:
workflowVersions and workflowRuns are heavy object. I'm preventing
loading their data when they are loaded as relations. This will reduce
the work on backend

## Long term fix

Todo:
We should make sure workflow data is ligher
This commit is contained in:
Charles Bochet
2025-10-06 23:53:58 +02:00
committed by GitHub
parent 5300ec5309
commit 0fd6085b53
7 changed files with 55 additions and 32 deletions
@@ -1,3 +1,4 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isDefined } from 'twenty-shared/utils';
@@ -17,7 +18,17 @@ export const generateDepthOneRecordGqlFields = ({
[field.settings.joinColumnName]: true,
}
: {}),
[field.name]: true,
[field.name]:
// TODO: Remove once we have made the workflows lighter
(objectMetadataItem.nameSingular ===
CoreObjectNameSingular.Workflow ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkflowVersion ||
objectMetadataItem.nameSingular ===
CoreObjectNameSingular.WorkflowRun) &&
(field.name === 'versions' || field.name === 'runs')
? { id: true, name: true }
: true,
};
},
{},
@@ -3,7 +3,7 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
import { recordCalendarSelectedRecordIdsComponentSelector } from '@/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector';
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordTableRecordGqlFields';
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordFieldGqlFields';
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
@@ -2,6 +2,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { type RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields';
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
@@ -37,38 +38,50 @@ export const useRecordFieldGqlFields = ({
objectMetadataItem,
});
const gqlFieldsList = Object.fromEntries(
visibleRecordFields.flatMap((recordField) => {
const fieldMetadataItem: FieldMetadataItem | undefined =
fieldMetadataItemByFieldMetadataItemId[recordField.fieldMetadataItemId];
const gqlFieldsList: RecordGqlFields = Object.fromEntries(
visibleRecordFields.flatMap(
(recordField): [string, boolean | RecordGqlFields][] => {
const fieldMetadataItem: FieldMetadataItem | undefined =
fieldMetadataItemByFieldMetadataItemId[
recordField.fieldMetadataItemId
];
if (!isDefined(fieldMetadataItem)) {
throw new Error(
`Field ${recordField.fieldMetadataItemId} is missing, please refresh the page. If the problem persists, please contact support.`,
);
}
if (!isDefined(fieldMetadataItem)) {
throw new Error(
`Field ${recordField.fieldMetadataItemId} is missing, please refresh the page. If the problem persists, please contact support.`,
);
}
const isMorphRelation =
fieldMetadataItem.type === FieldMetadataType.MORPH_RELATION;
// TODO: remove this once we have made the workflowVersion lighter
if (
fieldMetadataItem.name === 'versions' ||
fieldMetadataItem.name === 'workflowVersion'
) {
return [[fieldMetadataItem.name, { id: true, name: true }]];
}
if (!isMorphRelation) {
return [[fieldMetadataItem.name, true]];
}
const isMorphRelation =
fieldMetadataItem.type === FieldMetadataType.MORPH_RELATION;
if (!isDefined(fieldMetadataItem.morphRelations)) {
throw new Error(
`Field ${fieldMetadataItem.name} is missing, please refresh the page. If the problem persists, please contact support.`,
);
}
if (!isMorphRelation) {
return [[fieldMetadataItem.name, true]];
}
return fieldMetadataItem.morphRelations.map((morphRelation) => [
morphRelation.sourceFieldMetadata.name,
true,
]);
}),
if (!isDefined(fieldMetadataItem.morphRelations)) {
throw new Error(
`Field ${fieldMetadataItem.name} is missing, please refresh the page. If the problem persists, please contact support.`,
);
}
return fieldMetadataItem.morphRelations.map((morphRelation) => [
morphRelation.sourceFieldMetadata.name,
true,
]);
},
),
);
const recordGqlFields: Record<string, any> = {
return {
...allDepthOneWithoutRelationsRecordGqlFields,
...gqlFieldsList,
noteTargets: generateDepthOneRecordGqlFields({
@@ -78,6 +91,4 @@ export const useRecordFieldGqlFields = ({
objectMetadataItem: taskTargetObjectMetadataItem,
}),
};
return recordGqlFields;
};
@@ -1,6 +1,6 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordTableRecordGqlFields';
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordFieldGqlFields';
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
export const useRecordIndexTableFetchMore = (objectNameSingular: string) => {
@@ -1,6 +1,6 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordTableRecordGqlFields';
import { useRecordFieldGqlFields } from '@/object-record/record-field/hooks/useRecordFieldGqlFields';
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
@@ -53,7 +53,7 @@ export const useActiveWorkflowVersionsWithManualTrigger = ({
const { records } = useFindManyRecords<
Pick<
ManualTriggerWorkflowVersion,
'id' | '__typename' | 'trigger' | 'status' | 'workflowId'
'id' | '__typename' | 'status' | 'workflowId' | 'trigger'
> & {
workflow: Workflow;
}
@@ -10,6 +10,7 @@ import { isDefined } from 'twenty-shared/utils';
export const useWorkflowWithCurrentVersion = (
workflowId: string | undefined,
): WorkflowWithCurrentVersion | undefined => {
// TODO: we should only load the data for the current version
const { record: workflow } = useFindOneRecord<Workflow>({
objectNameSingular: CoreObjectNameSingular.Workflow,
objectRecordId: workflowId,