Skip to content

Bug: ~1.5x slowdown in Sentry codebase from project service #8424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
4 tasks done
JoshuaKGoldberg opened this issue Feb 10, 2024 · 4 comments
Closed
4 tasks done

Bug: ~1.5x slowdown in Sentry codebase from project service #8424

JoshuaKGoldberg opened this issue Feb 10, 2024 · 4 comments
Assignees
Labels
bug Something isn't working locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. performance Issues regarding performance team assigned A member of the typescript-eslint team should work on this.
Milestone

Comments

@JoshuaKGoldberg
Copy link
Member

JoshuaKGoldberg commented Feb 10, 2024

Before You File a Bug Report Please Confirm You Have Done The Following...

  • I have tried restarting my IDE and the issue persists.
  • I have updated to the latest version of the packages.
  • I have searched for related issues and found none that matched my issue.
  • I have read the FAQ and my problem is not listed.

Issue Description

Switching from parserOptions.project: true to parserOptions.EXPERIMENTAL_useProjectService should in the worst case scenario should be roughly the same speed as before. The best case scenario should be faster. But checking it out on the getsentry/sentry project, I'm seeing a raise from ~50s/65s to ~80s/110s. 😬

Baseline:

 $ time yarn lint:js                    
yarn run v1.22.19
$ eslint static/app tests/js --ext .js,.jsx,.ts,.tsx
✨  Done in 49.47s.
yarn lint:js  65.43s user 3.26s system 138% cpu 49.607 total

With the project service:

$ time yarn lint:js
yarn run v1.22.19
$ eslint static/app tests/js --ext .js,.jsx,.ts,.tsx
✨  Done in 81.42s.
yarn lint:js  108.11s user 8.10s system 142% cpu 1:21.55 total

Thanks @yagiz for reporting!

Reproduction Repository Link

https://github.com/getsentry/sentry/pull/64967/files#diff-e2954b558f2aa82baff0e30964490d12942e0e251c1aa56c3294de6ec67b7cf5R28-R30

Repro Steps

  1. Clone getsentry/sentry
  2. time yarn lint:js
  3. Enable parserOptions.EXPERIMENTAL_useProjectService
  4. time yarn lint:js

Versions

package version
@typescript-eslint/eslint-plugin 6.19.0
@typescript-eslint/parser 6.19.0
TypeScript 5.3.2
ESLint 8.49.0
node 20
@JoshuaKGoldberg JoshuaKGoldberg added bug Something isn't working triage Waiting for team members to take a look performance Issues regarding performance labels Feb 10, 2024
@JoshuaKGoldberg JoshuaKGoldberg added this to the 8.0.0 milestone Feb 10, 2024
@JoshuaKGoldberg JoshuaKGoldberg self-assigned this Feb 10, 2024
@JoshuaKGoldberg
Copy link
Member Author

JoshuaKGoldberg commented Feb 10, 2024

First finding: this is an example of an existing performance issue with eslint-plugin-import. @anonrig we recommend disabling the eslint-plugin-import rules mentioned in that page, as many of them duplicate work TypeScript already does and/or don't play well with our tooling.

Disabling the import/* rules as recommended improves lint time for both:

  • Baseline: ✨ Done in 36.57s. \n yarn lint:js 51.59s user 1.82s system 145% cpu 36.695 total
  • Project service: ✨ Done in 58.75s. \n yarn lint:js 80.06s user 5.38s system 145% cpu 58.866 total

More investigation to be done for the discrepancy, still. And maybe issue(s) to be filed on better detecting/handling this common scenario and/or playing better with eslint-plugin-import.

Full investigation for the curious...

Looking at logs generated with DEBUG=typescript-eslint:* yarn lint:js in 3b2040a: Sentry Debug Logs.txt...

node_modules/@types .d.ts files are having the project service called for them too. Not just source .ts/.tsx files.

$ eslint static/app tests/js --ext .js,.jsx,.ts,.tsx
  typescript-eslint:typescript-estree:useProgramFromProjectService Opening project service file for: /users/josh/repos/sentry/static/app/__mocks__/api.tsx +0ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opened project service file: { configFileName: '/users/josh/repos/sentry/tsconfig.json', configFileErrors: [] } +5s
  typescript-eslint:typescript-estree:useProgramFromProjectService Found project service program for: /users/josh/repos/sentry/static/app/__mocks__/api.tsx +1ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /Users/josh/repos/sentry/static/app/__mocks__/api.tsx +0ms
  typescript-eslint:parser:parser Resolved libs from program: [ 'esnext', 'dom' ] +0ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opening project service file for: /users/josh/repos/sentry/node_modules/@types/lodash/isequal.d.ts +234ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opened project service file: { configFileName: undefined, configFileErrors: undefined } +2ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Found project service program for: /users/josh/repos/sentry/node_modules/@types/lodash/isequal.d.ts +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /Users/josh/repos/sentry/node_modules/@types/lodash/isEqual.d.ts +236ms
  typescript-eslint:parser:parser Resolved libs from program: [ 'esnext', 'dom' ] +216ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opening project service file for: /users/josh/repos/sentry/static/app/__mocks__/prismjs.tsx +32ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opened project service file: { configFileName: '/users/josh/repos/sentry/tsconfig.json', configFileErrors: undefined } +2ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Found project service program for: /users/josh/repos/sentry/static/app/__mocks__/prismjs.tsx +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /Users/josh/repos/sentry/static/app/__mocks__/prismjs.tsx +34ms
  typescript-eslint:parser:parser Resolved libs from program: [ 'esnext', 'dom' ] +34ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opening project service file for: /users/josh/repos/sentry/node_modules/@types/prismjs/components.d.ts +10ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Opened project service file: { configFileName: undefined, configFileErrors: undefined } +2ms
  typescript-eslint:typescript-estree:useProgramFromProjectService Found project service program for: /users/josh/repos/sentry/node_modules/@types/prismjs/components.d.ts +0ms
  typescript-eslint:typescript-estree:createProjectProgram Creating project program for: /Users/josh/repos/sentry/node_modules/@types/prismjs/components.d.ts +12ms
  typescript-eslint:parser:parser Resolved libs from program: [ 'esnext', 'dom' ] +11ms
...
  • sentry/static/app/__mocks__/api.tsx +0ms: expected
  • sentry/node_modules/@types/lodash/isequal.d.ts +234ms: unexpected 🤔
  • sentry/static/app/__mocks__/prismjs.tsx +32ms: expected
  • sentry/node_modules/@types/prismjs/components.d.ts +10ms: unexpected 🤔

Running DEBUG=eslint:cli-engine yarn lint:js shows that ESLint isn't trying to load those files. From Sentry Debug Logs - eslint-cli-engine.txt:

$ eslint static/app tests/js --ext .js,.jsx,.ts,.tsx
  eslint:cli-engine Lint /Users/josh/repos/sentry/static/app/__mocks__/api.tsx +0ms
  eslint:cli-engine Lint /Users/josh/repos/sentry/static/app/__mocks__/prismjs.tsx +5s
  eslint:cli-engine Lint /Users/josh/repos/sentry/static/app/__mocks__/react-date-range.tsx +16ms
...

Why is the parser being called for these files? With a console.log(new Error().stack):

Error
    at useProgramFromProjectService (/Users/josh/repos/typescript-eslint/packages/typescript-estree/dist/useProgramFromProjectService.js:16:21)
    at getProgramAndAST (/Users/josh/repos/typescript-eslint/packages/typescript-estree/dist/parser.js:38:100)
    at parseAndGenerateServices (/Users/josh/repos/typescript-eslint/packages/typescript-estree/dist/parser.js:162:11)
    at Object.parseForESLint (/Users/josh/repos/typescript-eslint/packages/parser/dist/parser.js:93:80)
    at parse (/Users/josh/repos/sentry/node_modules/eslint-module-utils/parse.js:97:32)
    at ExportMap.parse (/Users/josh/repos/sentry/node_modules/eslint-plugin-import/lib/ExportMap.js:808:420)
    at ExportMap.for (/Users/josh/repos/sentry/node_modules/eslint-plugin-import/lib/ExportMap.js:807:201)
    at ExportMap.get (/Users/josh/repos/sentry/node_modules/eslint-plugin-import/lib/ExportMap.js:801:465)
    at checkDefault (/Users/josh/repos/sentry/node_modules/eslint-plugin-import/lib/rules/default.js:22:46)
    at ruleErrorHandler (/Users/josh/repos/sentry/node_modules/eslint/lib/linter/linter.js:1051:28)

😱 eslint-plugin-import! Specifically, import/default.

What's happening is:

  1. The sentry codebase has eslint-plugin-import's import/default rule enabled to ensure that when an imported module has a default export, that export is default-imported
  2. In order to determine whether an imported module has a default export, the rule does an out-of-band parse using eslint-module-utils/parse
  3. That parse calls to the same parser, languageOptions, etc. that ESLint is normally called with
  4. Our parser doesn't know this call is coming from not-ESLint and type information doesn't need to be generated

...and thus our parser tries to create type information on .d.ts files.

@JoshuaKGoldberg JoshuaKGoldberg changed the title Bug: 2x slowdown in Sentry codebase from project service Bug: ~1.5x slowdown in Sentry codebase from project service Feb 10, 2024
@bradzacher
Copy link
Member

Oh @JoshuaKGoldberg we can fix that!
https://github.com/import-js/eslint-plugin-import/blob/7a21f7e10f18c04473faadca94928af6b8e28009/utils%2Fparse.js#L80-L85

I'd forgotten that I'd added this to their setup.
We just need to add more deletes.
Perhaps we should add a util function to our project so we can control this without touching their code??

@JoshuaKGoldberg
Copy link
Member Author

Aha! I didn't know about that - awesome! Filed #8428.

@JoshuaKGoldberg JoshuaKGoldberg added the team assigned A member of the typescript-eslint team should work on this. label Jun 2, 2024
@Josh-Cena Josh-Cena removed the triage Waiting for team members to take a look label Jun 4, 2024
@JoshuaKGoldberg JoshuaKGoldberg changed the title Bug: ~1.5x slowdown in Sentry codebase from project service ⚡️ Performance: Slowdowns switching from project to projectService Jul 18, 2024
@JoshuaKGoldberg JoshuaKGoldberg changed the title ⚡️ Performance: Slowdowns switching from project to projectService Bug: ~1.5x slowdown in Sentry codebase from project service Jul 18, 2024
@JoshuaKGoldberg
Copy link
Member Author

This bug is mostly info specific to Sentry and the import plugin+rules. I filed #9571 to track general performance concerns with projectService. Closing this one for now. Thanks everyone for helping us know of -> investigate these issues!

#9571 (comment) mentions two TypeScript issues tracking performance regressions.

@github-actions github-actions bot added the locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. label Jul 27, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 27, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working locked due to age Please open a new issue if you'd like to say more. See https://typescript-eslint.io/contributing. performance Issues regarding performance team assigned A member of the typescript-eslint team should work on this.
Projects
None yet
Development

No branches or pull requests

3 participants