Files
upstream-twenty/packages/twenty-server/test/integration/metadata/suites/application/logic-function-install-performance.integration-spec.ts
upstream-mirror e7da97194a
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

149 lines
4.8 KiB
TypeScript

import { buildBaseManifest } from 'test/integration/metadata/suites/application/utils/build-base-manifest.util';
import { cleanupApplicationAndAppRegistration } from 'test/integration/metadata/suites/application/utils/cleanup-application-and-app-registration.util';
import { setupApplicationForSync } from 'test/integration/metadata/suites/application/utils/setup-application-for-sync.util';
import { syncApplication } from 'test/integration/metadata/suites/application/utils/sync-application.util';
import {
type LogicFunctionManifest,
type Manifest,
} from 'twenty-shared/application';
import { v4 as uuidv4 } from 'uuid';
/**
* Performance harness for installing / updating many logic functions through
* application manifest sync. The goal is to find what could take >10s in prod
* (which trips the node-postgres `query_timeout` in core.datasource.ts and
* produces "Migration action 'update' for 'logicFunction' failed" + 504).
*
* IMPORTANT: the integration harness boots the NestJS app in-process with
* `fakeTimers.enableGlobally: true`. We call `jest.useRealTimers()` for the whole
* suite so timing (`performance.now()`) is real and cache-lock retry delays etc.
* do not hang.
*/
// Real timers for the whole suite — see note above.
jest.useRealTimers();
jest.setTimeout(120000);
const FN_COUNTS = [1, 8, 30];
// Stable universalIdentifiers across versions so the second sync exercises
// UPDATE (the actual incident), not CREATE+DELETE.
const buildManifest = ({
appId,
roleId,
universalIdentifiers,
checksumVersion,
}: {
appId: string;
roleId: string;
universalIdentifiers: string[];
checksumVersion: 'v1' | 'v2';
}): Manifest => {
const logicFunctions: LogicFunctionManifest[] = universalIdentifiers.map(
(universalIdentifier, i) => ({
universalIdentifier,
name: `PerfFn${i}`,
description: `Perf logic function ${i}`,
handlerName: 'handler',
sourceHandlerPath: `src/fn-${i}.ts`,
builtHandlerPath: `dist/fn-${i}.mjs`,
builtHandlerChecksum: `checksum-${i}-${checksumVersion}`,
httpRouteTriggerSettings: {
path: `/fn-${i}`,
httpMethod: 'GET',
isAuthRequired: true,
},
}),
);
return buildBaseManifest({
appId,
roleId,
overrides: { logicFunctions },
});
};
const timeSync = async (label: string, manifest: Manifest): Promise<number> => {
const start = performance.now();
await syncApplication({ manifest, expectToFail: false });
const ms = performance.now() - start;
// oxlint-disable-next-line no-console
console.log(`[install-perf][test] ${label} took ${ms.toFixed(1)}ms`);
return ms;
};
// TODO(install-perf): temporary manual perf harness, remove. Skipped in CI.
describe.skip('Logic function install performance', () => {
it.each(FN_COUNTS)(
'create + update sync with %i logic functions',
async (count) => {
const appId = uuidv4();
const roleId = uuidv4();
const universalIdentifiers = Array.from({ length: count }, () =>
uuidv4(),
);
await setupApplicationForSync({
applicationUniversalIdentifier: appId,
name: `Perf App ${count}`,
description: `Perf app with ${count} logic functions`,
sourcePath: `perf-app-${count}`,
});
jest.useRealTimers();
try {
// No built-handler file upload is needed: the migration create/update
// handlers never read the built file for LIVE functions (prebuilt
// install is skipped), and uploading N files would trip the file-upload
// rate limiter (30 per 30s). We only measure migration + cache cost.
// oxlint-disable-next-line no-console
console.log(
`[install-perf][test] ===== N=${count} : FIRST SYNC (create ${count} functions) =====`,
);
const createMs = await timeSync(
`N=${count} create sync`,
buildManifest({
appId,
roleId,
universalIdentifiers,
checksumVersion: 'v1',
}),
);
// oxlint-disable-next-line no-console
console.log(
`[install-perf][test] ===== N=${count} : SECOND SYNC (update ${count} functions, checksum v2) =====`,
);
const updateMs = await timeSync(
`N=${count} update sync`,
buildManifest({
appId,
roleId,
universalIdentifiers,
checksumVersion: 'v2',
}),
);
// oxlint-disable-next-line no-console
console.log(
`[install-perf][test] ===== N=${count} SUMMARY: create=${createMs.toFixed(1)}ms update=${updateMs.toFixed(1)}ms =====`,
);
} finally {
await cleanupApplicationAndAppRegistration({
applicationUniversalIdentifier: appId,
});
}
},
120000,
);
});