Skip to content

Connection Denied when Running Multiple NPM Tasks in the Same Project #2426

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

Open
jGleitz opened this issue Feb 19, 2025 · 2 comments
Open

Connection Denied when Running Multiple NPM Tasks in the Same Project #2426

jGleitz opened this issue Feb 19, 2025 · 2 comments
Labels

Comments

@jGleitz
Copy link
Contributor

jGleitz commented Feb 19, 2025

We have multiple prettier-based tasks in our project, and we occasionally observe java.net.ConnectException: connection denied. This only happens in subprojects where multiple file types need to be formatted and use the same npm packages.

I’m not sure about the root cause, but I have a theory: NpmFormatterStepStateBase#npmRunServer has a race condition that would cause the behaviour we’re seeing.

The method works as follows:

  1. Check the server.port file in the installation directory, delete it if it exists.
  2. Start the Node.JS server.
  3. Wait for the server.port file to exist.
  4. Check the connection to the server using the port from server.port.

Crucially, two prettier tasks that use the same npm modules will share the installation directory. So we get the following race condition. I’ll explain using the example of a spotlessJson and a spotlessYaml task:

  1. spotlessJson and spotlessYaml start (approximately) simultaneously.
  2. spotlessJson and spotlessYaml pass the server.port file check before the other has started the server.
  3. spotlessJson and spotlessYaml start the NPM server.
  4. spotlessJson’s (for example) server starts first and creates server.port
  5. One of the two tasks sees the server.port, uses the server to format their files, and shuts down the server.
  6. The other task sees the server.port, tries to use the server, but can’t because the server has already shut down (it may even succeed in formatting some files).

@nedtwigg what do you think? Does this race condition exist or did I overlook something?

I’d consider contributing a fix if we agree that this is a likely root cause.

@jGleitz
Copy link
Contributor Author

jGleitz commented Feb 19, 2025

We applied this workaround, which seems to fix the issue for us. It restricts Gradle to execute only one prettier-based spotless task in parallel per project. This workaround is consistent with the theory about the race condition:

val spotlessNpmService =
  gradle.sharedServices.registerIfAbsent("spotlessNpm-" + project.path, SpotlessNpmService::class) {
    maxParallelUsages = 1
  }

afterEvaluate {
  // we must do this in afterEvaluate because before that, the SpotlessExtension will not have
  // configured the task yet.
  tasks.withType<SpotlessTask>().configureEach {
    if (stepsInternalRoundtrip.steps.any { it.name == "prettier-format" }) {
      usesService(spotlessNpmService)
    }
  }
}

@jGleitz jGleitz changed the title Connection Denied if Multiple NPM Tasks is the Same Modules Connection Denied when Running Multiple NPM Tasks in the Same Project Feb 19, 2025
@nedtwigg nedtwigg added the bug label Feb 19, 2025
@nedtwigg
Copy link
Member

Great investigation, I think you are correct about the cause. Happy to merge a PR with a workaround or deeper fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants