Skip to content
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

Persistent tasks in watch mode don't wait for first run of their dependencies to finish #8673

Open
1 task done
OliverJAsh opened this issue Jul 5, 2024 · 12 comments
Open
1 task done
Labels
kind: bug Something isn't working needs: champion

Comments

@OliverJAsh
Copy link

OliverJAsh commented Jul 5, 2024

Verify canary release

  • I verified that the issue exists in the latest Turborepo canary release.

Link to code that reproduces this issue

https://github.com/OliverJAsh/turbo-watch-deps-of-persistent

What package manager are you using / does the bug impact?

Yarn v2/v3/v4 (node_modules linker only)

What operating system are you using?

Mac

Which canary version will you have in your reproduction?

2.0.6-canary.0

Describe the Bug

Persistent tasks in watch mode don't wait for first run of their dependencies to finish.

  • Package b depends on package a.
  • Both packages have a build task.

In non-watch / run mode, build a correctly runs before build b:

$ TURBO_UI=false yarn run turbo run build --filter b
• Packages in scope: b
• Running build in 1 packages
• Remote caching disabled
a:build: cache bypass, force executing 7c09bd93554e1d1c
a:build: A
b:build: cache bypass, force executing 7f551e27d3b8aa66
b:build: B

 Tasks:    2 successful, 2 total
Cached:    0 cached, 2 total
  Time:    1.516s

However, in watch mode, Turbo runs both builds in parallel. Build b does not wait for build a to finish even though it's a dependency:

$ TURBO_UI=false yarn run turbo watch build --filter b
• Packages in scope: b
• Running build in 1 packages
• Remote caching disabled
• Packages in scope: b
• Running build in 1 packages
• Remote caching disabled
a:build: cache bypass, force executing 7c09bd93554e1d1c
b:build: cache bypass, force executing 25c5580a26314aac
b:build: B
a:build: A

Expected Behavior

Persistent tasks in watch mode should wait for the first run of their dependencies to finish, otherwise they may fail.

To Reproduce

  1. Clone https://github.com/OliverJAsh/turbo-watch-deps-of-persistent.
  2. Run yarn.
  3. Run TURBO_UI=false yarn run turbo watch build --filter b.

Additional context

I believe the idea is that persistent tasks should self-heal: #8164 (comment)

However, this is inefficient and can lead to confusing error messages.

In our case we're using webpack watch and rebuilding is CPU intensive, so it would be better if webpack waited for its dependencies to finish building before starting.

Furthermore, our webpack watch task can only self-heal after the build has started—it can't self-heal if the configuration itself is missing dependencies.

We only expect the order to be respected for the first run, not subsequent to that.

@OliverJAsh OliverJAsh added kind: bug Something isn't working needs: triage New issues get this label. Remove it after triage owned-by: turborepo labels Jul 5, 2024
@NicholasLYang NicholasLYang added needs: champion and removed needs: triage New issues get this label. Remove it after triage labels Jul 9, 2024
@NicholasLYang
Copy link
Contributor

Hi, thanks for the issue. We currently don't have the capacity to work on this currently. If you want, you're welcome to open a PR for it and I can provide guidance.

@Patrick-Ullrich
Copy link

Patrick-Ullrich commented Nov 9, 2024

@NicholasLYang IMHO this feels like a huge blocker of turborepo adoption. Given it isn't a priority I feel like I am missing something. As someone trying to use turbo watch since it showed up in canary I don't quite understand what it is currently solving without this. Is there an example repository somewhere that would show how someone can leverage turbo watch?

If i am understanding correctly, the usual set up is:

  • apps with persistent dev tasks (next, vite, etc.)
  • packages with a build step

Whenever a package changes, the dependent apps need to know about it. Without it, one would have to restart turbo dev on every change.

I tried various work arounds:

  • make dev dependent on dev and add a bundler to every package that supports some sort of watch mode, but you can't make persistent tasks dependent on other persistent tasks
  • remove persistent, and remove all hot reloads, hasn't been very succesful

The best solution for now has been using packages without a build step and just export the ts files directly as much as possible and let the host apps bundler bundle the package instead.

Appreciate any guidance / insight!

@NicholasLYang
Copy link
Contributor

Hi @Patrick-Ullrich, sorry for the late reply! Could you try using the new interruptible flag? This sort of does what you want, where we now include persistent tasks in the regular task graph if they're marked as interruptible. The trade-off is that we also restart them when we encounter a change. Depending on your use case, that could be what you want.

@Patrick-Ullrich
Copy link

Patrick-Ullrich commented Nov 14, 2024

@NicholasLYang That sounds like it would do the trick! Part of 2.2.3?

Should it work like this:

  "dev": {
      "dependsOn": [
        "^build"
      ],
      "cache": false,
      "persistent": true,
      "interruptible": true
    },

Also happy to take this conversation somewhere else if we are getting too off topic!

Screenshot 2024-11-13 at 8 07 31 PM

@KajSzy
Copy link

KajSzy commented Nov 14, 2024

I'm trying to cover very similar case where packages are build while main app uses webpack dev server.
During watch mode packages are properly built and re-build but as soon as they are done webpack is being shut down and restarted.
Tried using interruptible flag but it nothing has changed

@gregnr
Copy link

gregnr commented Nov 14, 2024

@KajSzy I experienced something similar to this and it turned out it was a bug in the latest v2.2.3 release where persistent tasks were accidently stopped in watch mode: #9330. Fixed in v2.2.4-canary.2+

@KajSzy
Copy link

KajSzy commented Nov 15, 2024

Yes, with 2.3 release it was fixed

@thejerrybao
Copy link

Is there a way to configure it so that only the non-persistent tasks are executed after the first run?

I have a configuration like so:

    "build": {
      "dependsOn": [
        "^build"
      ],
      "outputs": [
        "dist/**"
      ]
    },
    "dev": {
      "dependsOn": ["^build"],
      "cache": false,
      "persistent": true,
      "interruptible": true,
    },

and while this does wait for the first run of dependencies to finish, subsequent changes to packages also runs dev again. dev for me is a Nest dev server that doesn't need to be rerun.

@theogravity
Copy link

theogravity commented Nov 19, 2024

I'm facing the same situation in [email protected] (also checked with [email protected]), except I have next.js in dev mode running along with an API server that auto-reloads on its own when something has changed.

This is without the interruptible flag - next.js / the API server runs at the same time as the dependencies build as opposed to the dependency build finishing first THEN starting the services.

    "build:dev": {
      "dependsOn": [
        "^build:dev"
      ],
      "cache": false
    },
    "dev": {
      "dependsOn": [
        "^build:dev"
      ],
      "cache": false,
      "persistent": true
    },

(I expect build:dev to complete first before running the items in dev)

Like the OP, turbo dev is fine, but turbo watch dev is bugged.

@theogravity
Copy link

theogravity commented Nov 19, 2024

I did an awful hack that involved the use of the wait-on package:

  • I created two packages (I call them lockfile packages), each representing my next.js and my API service, respectively
  • In each package, I added the following command to the package.json: "build:dev": "rm -rf .lockfile",
    • The packages have the dependencies that needed to be built listed as devDependencies before that service can start
  • I installed wait-on as a devDep in my next.js and API service
  • I augmented the dev script on both next.js and API service with touch ../../packages/<service-name>-lock/.lockfile && wait-on -r ../../packages/<service-name>-lock/.lockfile && <service start watch command>

So what happens when I run the turbo watch dev command:

  • Because of this bug, the services will run concurrently to the dependency build. However, when the service runs, they will create .lockfile and then wait for that .lockfile to be removed before actually starting the server
  • The packages will build, and because the lockfile packages have those packages as a dependency, it will only remove the lockfile once those dependent packages have been built
  • Because the lockfile is now removed, then the services will start with the correctly built dependencies

@dylanjmcdonald
Copy link

dylanjmcdonald commented Dec 13, 2024

There still seems to be something going on with watch. I'm following the kitchen sink api example and either turbo watch or tsup --watch isn't detecting changes.

Screenshot 2024-12-13 at 2 14 05 PM

@dmstoykov
Copy link

dmstoykov commented Jan 8, 2025

@dylanjmcdonald In the context of the kitchen sink example, if you change the code inside of @repo/logger the api tsup --watch won't detect the change as its not aware when one of its dependencies change.

This led me to then make the following changes:

  1. Change the dev script in the root package.json to turbo watch dev
  2. Delete the dev script inside of the packages/logger and packages/ui because we are using the turbo watch command now
  3. Make the dev task inside of api interruptible so that the api server is restarted when its dependencies change

This leaves us with the following setup:

  1. Apps inside the apps/ directory have dev scripts that are both persistent but only apps/api is interruptible
  2. Packages inside the packages/ directory have a build script and rely on turbo watch to be rebuilt

However, with such setup (which i believe is an extremely common one in monorepos) there is a bug currently ([email protected]).

Repro steps:

  1. Update the code inside packages/ui package
  2. packages/ui gets rebuilt (as it should)
  3. The interruptible api dev task gets terminated and isn't restarted.

I believe this bug is documented here => #9421

I haven't been able to find a good workaround for this issue.

UPDATE:
A neat workaround that I found was to mark all persistent tasks as non-interruptible and use the watch command from tsx to monitor and restart those tasks. tsx watch (v4.19.2) also detects changes inside internal package dependencies of the watched package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: bug Something isn't working needs: champion
Projects
None yet
Development

No branches or pull requests

10 participants