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

Multiple watchers create multiple downloads and a conflict when installing #58

Closed
azizk opened this issue Apr 28, 2023 · 3 comments · Fixed by #59
Closed

Multiple watchers create multiple downloads and a conflict when installing #58

azizk opened this issue Apr 28, 2023 · 3 comments · Fixed by #59

Comments

@azizk
Copy link
Contributor

azizk commented Apr 28, 2023

Given the following config.exs:

config :project, ProjectWeb.Endpoint,
  http: [ip: {0, 0, 0, 0}, port: 4000],
  debug_errors: true,
  code_reloader: true,
  cache_static_lookup: false,
  check_origin: false,
  watchers: [
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    another: {Esbuild, :install_and_run, [:another, ~w(--sourcemap=inline --watch)]}
  ]

When the application is started with iex -S mix phx.server,
the watchers are started concurrently and they try to download and install esbuild at the same time.
My educated guess is that one of them succeeds, but the other one encounters the installed binary and raises this error:

[error] Task #PID<0.1147.0> started from ProjectWeb.Endpoint terminating
** (File.CopyError) could not copy from "/home/work/.cache/phx-esbuild/package/bin/esbuild" to "/home/work/src/project/_build/esbuild-linux-x64": text file or pseudo-device busy
    (elixir 1.14.2) lib/file.ex:838: File.cp!/3
    (esbuild 0.7.0) lib/esbuild.ex:192: Esbuild.install_and_run/2
    (phoenix 1.6.15) lib/phoenix/endpoint/watcher.ex:19: Phoenix.Endpoint.Watcher.watch/2
    (elixir 1.14.2) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
    (stdlib 3.17.2.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Function: &Phoenix.Endpoint.Watcher.watch/2
    Args: ["another", {Esbuild, :install_and_run, [:another, ["--sourcemap=inline", "--watch"]]}]

The two esbuild profiles exist because they are separate and produce different outputs and therefore cannot be handled by one esbuild invocation.

@cw789
Copy link
Contributor

cw789 commented Apr 28, 2023

Use :run instead of :install_and_run on all others should be enough.

watchers: [
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
    another: {Esbuild, :run, [:another, ~w(--sourcemap=inline --watch)]}
]

@azizk
Copy link
Contributor Author

azizk commented Apr 28, 2023

@cw789 That sounded like a great solution at first but then I thought how is the second instance going to run when it hasn't been downloaded and installed yet by the first watcher? 🤷 😄

I think the right solution is to write a little DownloadGenServer that runs for all watchers and notifies them when the installation is complete. What do you think?

@josevalim
Copy link
Member

I think we can ship with a supervision tree and spawn a process with a name (or ID). If the name is taken, it is because it is already running. So we don't need a GenServer per se, just the supervisor is enough. PRs welcome!

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

Successfully merging a pull request may close this issue.

3 participants