nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.28k stars 2.32k forks source link

perf: ⚡ Nx 16.6.x and 16.7.x both show 300 - 400% exponential build time increase #18645

Closed bjornharvold closed 1 year ago

bjornharvold commented 1 year ago

Current Behavior

I don't consider this a bug. Nx builds fine. But the last 2 version of Nx (at latest with Angular) build times have increased significantly.

✔  nx run key-value-domain:build:production (6s)
✔  nx run shared-util-tokens:build:production (6s)
✔  nx run shared-ui-ghost-loading-element:build:production (8s)
✔  nx run authentication-util-authenticated-guard:build:production (6s)
✔  nx run shared-ui-no-result-box:build:production (7s)
✔  nx run lifestyle-domain:build:production (6s)
✔  nx run currency-domain:build:production (5s)
✔  nx run crud-publisher:build:production (4s)
✔  nx run shared-ui-loading-indicator:build:production (8s)
✔  nx run form-publisher:build:production (4s)
✔  nx run shared-ui-create-button:build:production (7s)
✔  nx run shared-ui-switch-form:build:production (7s)
✔  nx run uuid-publisher:build:production (6s)
✔  nx run shared-ui-required-field-indicator:build:production (6s)
✔  nx run toast-domain:build:production (6s)
✔  nx run media-util-cloudinary-url-pipe:build:production (4s)
✔  nx run shared-ui-hint-card:build:production (7s)
✔  nx run dynamic-component-loader-domain:build:production (7s)
✔  nx run persistent-entity-publisher:build:production (6s)
✔  nx run google-maps-domain:build:production (6s)
✔  nx run currency-util-formatted-currency-pipe:build:production (6s)

Expected Behavior

Build times on Nx 16.5.3 on identical code base

✔ nx run key-value-domain:build:production (2s) ✔ nx run shared-util-tokens:build:production (2s) ✔ nx run shared-ui-ghost-loading-element:build:production (2s) ✔ nx run authentication-util-authenticated-guard:build:production (2s) ✔ nx run lifestyle-domain:build:production (2s) ✔ nx run shared-ui-no-result-box:build:production (2s) ✔ nx run currency-domain:build:production (1s) ✔ nx run shared-ui-loading-indicator:build:production (2s) ✔ nx run shared-ui-create-button:build:production (2s) ✔ nx run shared-ui-switch-form:build:production (2s) ✔ nx run crud-publisher:build:production (2s) ✔ nx run form-publisher:build:production (2s) ✔ nx run uuid-publisher:build:production (2s) ✔ nx run shared-ui-hint-card:build:production (2s) ✔ nx run shared-ui-required-field-indicator:build:production (2s) ✔ nx run dynamic-component-loader-domain:build:production (1s) ✔ nx run persistent-entity-publisher:build:production (1s) ✔ nx run media-util-cloudinary-url-pipe:build:production (2s) ✔ nx run google-maps-domain:build:production (2s) ✔ nx run currency-util-formatted-currency-pipe:build:production (2s)

GitHub Repo

No response

Steps to Reproduce

  1. Install a new Nx Angular Monorepo Package workspace @ v16.5.3 and build
  2. Install a new Nx Angular Monorepo Package workspace @ > v16.6.x and build

Compare the difference in build time

Nx Report

Including Nx both reports:

>  NX   Report complete - copy this into the issue template

   Node   : 18.17.1
   OS     : darwin-arm64
   yarn   : 1.22.19

   nx                 : 16.5.3
   @nx/js             : 16.5.3
   @nx/jest           : 16.5.3
   @nx/linter         : 16.5.3
   @nx/workspace      : 16.5.3
   @nx/angular        : 16.5.3
   @nx/cypress        : 16.5.3
   @nx/devkit         : 16.5.3
   @nx/eslint-plugin  : 16.5.3
   @nx/storybook      : 16.5.3
   @nrwl/tao          : 16.5.3
   @nx/web            : 16.5.3
   @nx/webpack        : 16.5.3
   typescript         : 5.1.6
   ---------------------------------------
   Community plugins:
   @compodoc/compodoc               : 1.1.21
   @fortawesome/angular-fontawesome : 0.13.0
   @jscutlery/semver                : 3.1.0
   @ng-bootstrap/ng-bootstrap       : 15.1.0
   @ngneat/transloco                : 5.0.1
   @ngrx/component                  : 16.1.0
   @ngrx/component-store            : 16.1.0
   @ngrx/data                       : 16.1.0
   @ngrx/effects                    : 16.1.0
   @ngrx/entity                     : 16.1.0
   @ngrx/router-store               : 16.1.0
   @ngrx/schematics                 : 16.1.0
   @ngrx/store                      : 16.1.0
   @ngrx/store-devtools             : 16.1.0
   @storybook/angular               : 7.1.1
   angular-calendar                 : 0.31.0

=====================

 >  NX   Report complete - copy this into the issue template

   Node   : 18.17.1
   OS     : darwin-arm64
   yarn   : 1.22.19

   nx                 : 16.7.0
   @nx/js             : 16.7.0
   @nx/jest           : 16.7.0
   @nx/linter         : 16.7.0
   @nx/workspace      : 16.7.0
   @nx/angular        : 16.7.0
   @nx/cypress        : 16.7.0
   @nx/devkit         : 16.7.0
   @nx/eslint-plugin  : 16.7.0
   @nx/storybook      : 16.7.0
   @nrwl/tao          : 16.7.0
   @nx/web            : 16.7.0
   @nx/webpack        : 16.7.0
   typescript         : 5.1.6
   ---------------------------------------
   Community plugins:
   @compodoc/compodoc               : 1.1.21
   @fortawesome/angular-fontawesome : 0.13.0
   @jscutlery/semver                : 3.1.0
   @ng-bootstrap/ng-bootstrap       : 15.1.1
   @ngneat/transloco                : 5.0.6
   @ngrx/component                  : 16.2.0
   @ngrx/component-store            : 16.2.0
   @ngrx/data                       : 16.2.0
   @ngrx/effects                    : 16.2.0
   @ngrx/entity                     : 16.2.0
   @ngrx/router-store               : 16.2.0
   @ngrx/schematics                 : 16.2.0
   @ngrx/store                      : 16.2.0
   @ngrx/store-devtools             : 16.2.0
   @storybook/angular               : 7.3.0
   angular-calendar                 : 0.31.0

Failure Logs

No response

Operating System

Additional Information

I mentioned this in the Nx Angular Slack channel here: https://nrwlcommunity.slack.com/archives/C016ASCS683/p1691660639502029

The last minor update came with a migration step that added something to the configuration of all modules and now this warning appears when building:

Option "updateBuildableProjectDepsInPackageJson" is deprecated: Configure the project to use the '@nx/dependency-checks' ESLint rule instead (https://nx.dev/packages/eslint-plugin/documents/dependency-checks). It will be removed in v17.

Is this the culprit?

FrozenPandaz commented 1 year ago

Hm, we need to look more into this.

Could you provide the executor being used to build the projects? Is it reproducible with a single project? If possible, could you please provide a repro?

bjornharvold commented 1 year ago

Hi @FrozenPandaz

Here's a project.json from one of our 10 apps (all configured the same way) and 2000+ module Nx monorepo.

{
  "name": "administration",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "sourceRoot": "apps/administration/src",
  "prefix": "wink",
  "targets": {
    "build": {
      "executor": "@nx/angular:webpack-browser",
      "outputs": ["{options.outputPath}"],
      "options": {
        "buildLibsFromSource": true,
        "allowedCommonJsDependencies": [
          "hammerjs",
          "clone-deep",
          "flat",
          "fast-sha256",
          "rfdc"
        ],
        "baseHref": "/",
        "outputPath": "dist/apps/administration",
        "index": "apps/administration/src/index.html",
        "main": "apps/administration/src/main.ts",
        "polyfills": ["zone.js"],
        "tsConfig": "apps/administration/tsconfig.app.json",
        "stylePreprocessorOptions": {
          "includePaths": []
        },
        "assets": [
          "apps/administration/src/assets",
          "apps/administration/src/manifest.json",
          "apps/administration/src/robots.txt"
        ],
        "styles": [
          "node_modules/npm-font-open-sans/open-sans.css",
          "apps/administration/src/assets/styles/traveliko.css",
          "shared-styles/administration/theme/default/styles.scss"
        ],
        "scripts": []
      },
      "configurations": {
        "production": {
          "fileReplacements": [
            {
              "replace": "apps/administration/src/environments/environment.ts",
              "with": "apps/administration/src/environments/environment.prod.ts"
            }
          ],
          "baseHref": "/",
          "optimization": {
            "scripts": true,
            "styles": {
              "minify": true,
              "inlineCritical": true
            },
            "fonts": true
          },
          "outputHashing": "all",
          "sourceMap": {
            "scripts": true,
            "hidden": true
          },
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "3mb",
              "maximumError": "5mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "25kb",
              "maximumError": "30kb"
            }
          ],
          "serviceWorker": true,
          "ngswConfigPath": "apps/administration/ngsw-config.json"
        },
        "staging": {
          "fileReplacements": [
            {
              "replace": "apps/administration/src/environments/environment.ts",
              "with": "apps/administration/src/environments/environment.staging.ts"
            }
          ],
          "baseHref": "/",
          "optimization": {
            "scripts": true,
            "styles": {
              "minify": true,
              "inlineCritical": true
            },
            "fonts": true
          },
          "outputHashing": "all",
          "sourceMap": {
            "scripts": true,
            "hidden": true
          },
          "namedChunks": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": true,
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "3mb",
              "maximumError": "5mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "25kb",
              "maximumError": "30kb"
            }
          ],
          "serviceWorker": true,
          "ngswConfigPath": "apps/administration/ngsw-config.json"
        },
        "development": {
          "buildOptimizer": false,
          "optimization": false,
          "vendorChunk": true,
          "extractLicenses": false,
          "sourceMap": true,
          "namedChunks": true
        }
      },
      "defaultConfiguration": "production"
    },
    "serve": {
      "executor": "@nx/angular:webpack-dev-server",
      "configurations": {
        "development": {
          "browserTarget": "administration:build:development"
        },
        "production": {
          "browserTarget": "administration:build:production"
        }
      }
    },
    "extract-i18n": {
      "executor": "@angular-devkit/build-angular:extract-i18n",
      "options": {
        "browserTarget": "administration:build"
      }
    },
    "lint": {
      "executor": "@nx/linter:eslint",
      "options": {
        "lintFilePatterns": [
          "apps/administration/src/**/*.ts",
          "apps/administration/src/**/*.html"
        ]
      },
      "outputs": ["{options.outputFile}"]
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/apps/administration"],
      "options": {
        "jestConfig": "apps/administration/jest.config.ts",
        "passWithNoTests": true
      }
    }
  },
  "tags": ["domain:administration", "type:app"]
}

Here's a project.json from one of our libraries (also configured identically):

{
  "name": "account-consumer",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "projectType": "library",
  "sourceRoot": "libs/account/consumer/src",
  "prefix": "wink",
  "targets": {
    "build": {
      "executor": "@nx/angular:ng-packagr-lite",
      "outputs": ["{workspaceRoot}/dist/libs/account/consumer"],
      "options": {
        "project": "libs/account/consumer/ng-package.json",
        "updateBuildableProjectDepsInPackageJson": true
      },
      "configurations": {
        "production": {
          "tsConfig": "libs/account/consumer/tsconfig.lib.prod.json"
        },
        "development": {
          "tsConfig": "libs/account/consumer/tsconfig.lib.json"
        }
      },
      "defaultConfiguration": "production"
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/libs/account/consumer"],
      "options": {
        "jestConfig": "libs/account/consumer/jest.config.ts",
        "passWithNoTests": true
      }
    },
    "lint": {
      "executor": "@nx/linter:eslint",
      "options": {
        "lintFilePatterns": [
          "libs/account/consumer/**/*.ts",
          "libs/account/consumer/**/*.html"
        ]
      }
    }
  },
  "tags": ["domain:account-consumer", "type:domain-consumer-logic"]
}

Are you saying by just creating a Angular package-based monorepo with the 2 versions mentioned above does not show you the difference in build times? If so, I will do my best to create something to reproduce this.

Cheers

Coly010 commented 1 year ago

Hey @bjornharvold

I tried to reproduce this but was unable to. If you're able to provide a reproduction repo, that would be very helpful

bjornharvold commented 1 year ago

Hi @Coly010

Sure thing. I looked into it and could also not replicate with a fresh install of Nx with 16.5.3 and 16.7.2 so I continued doing some digging.

  1. I removed the updateBuildableProjectDepsInPackageJson property which got added by the migrations.json in 16.6.0. I don't know why it was added in the first place. It's marked as deprecated and build works fine without it.
  2. I updated my namedInputs in nx.json. That section had never been touched manually. It had changed

From:

"namedInputs": {
    "default": [
      "{projectRoot}/**/*",
      "sharedGlobals"
    ],
    "sharedGlobals": [
      "{workspaceRoot}/workspace.json"
    ],
    "production": [
      "default",
      "!{projectRoot}/.storybook/**/*",
      "!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)",
      "!{projectRoot}/tsconfig.storybook.json",
      "!{projectRoot}/src/test-setup.[jt]s"
    ]
  }

To (taken from the fresh install sans the storybook stuff:

"namedInputs": {
    "default": [
      "{projectRoot}/**/*",
      "sharedGlobals"
    ],
    "sharedGlobals": [
      "{workspaceRoot}/workspace.json"
    ],
    "production": [
      "default",
      "!{projectRoot}/.storybook/**/*",
      "!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)",
      "!{projectRoot}/tsconfig.storybook.json",
      "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
      "!{projectRoot}/tsconfig.spec.json",
      "!{projectRoot}/jest.config.[jt]s",
      "!{projectRoot}/src/test-setup.[jt]s",
      "!{projectRoot}/.eslintrc.json"
    ]
  }

But I did also find the real issue: We have a 2000+ module monorepo with 11 apps and the rest libraries. If you type:

npx nx run-many -t build

It will take over a minute to read in the entire project graph in memory before Nx starts building anything. When it does start building, you'll see build times like these:

✔  nx run shared-util-tokens:build:production (7s)
✔  nx run key-value-domain:build:production (8s)
✔  nx run shared-ui-ghost-loading-element:build:production (9s)

If we, instead, execute a more limited build like this:

npx nx run-many -t build -p app1

It takes less than 25% of the time to read the project graph in memory and build times look like this:

✔  nx run shared-util-tokens:build:production (3s)
✔  nx run key-value-domain:build:production (3s)
✔  nx run shared-ui-ghost-loading-element:build:production (3s)

If we start adding projects to -p, it starts slowing down again. So, I do think that, due to the size of the monorepo AND a change in your codebase from 16.5.3 to v16.6.x that caused our build to slow down. And, I think it has something to do with the in-memory project graph / cache manager.

The shorty-term solution is just to build our apps individually and leverage the Nx Cache.

I'll let you guys do with ticket as you wish. Our build is back up to speed at least.

Coly010 commented 1 year ago

Glad to hear your build speed is back to normal!

But thank you for the information, this is very helpful.

When I tried it out locally, i used the updateBuildableProjectDepsInPackageJson property also, but couldn't see any difference, so I think you could be closer to the issue with it being related to the project-graph.

It's another avenue for me to investigate at least. Thank you!

github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it hasn't had any recent activity. It will be closed in 14 days if no further activity occurs. If we missed this issue please reply to keep it active. Thanks for being a part of the Nx community! 🙏

github-actions[bot] commented 11 months ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.