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

feat(nuxi): cli wrapper for self restart #18641

Merged
merged 9 commits into from Mar 3, 2023
Merged

feat(nuxi): cli wrapper for self restart #18641

merged 9 commits into from Mar 3, 2023

Conversation

antfu
Copy link
Member

@antfu antfu commented Jan 31, 2023

πŸ”— Linked issue

❓ Type of change

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

This would allow Nuxt to do hard restart, for self upgrading or other stuffs.

πŸ“ Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@codesandbox
Copy link

codesandbox bot commented Jan 31, 2023

CodeSandbox logoCodeSandbox logoΒ  Open in CodeSandbox Web Editor | VS Code | VS Code Insiders

@antfu antfu changed the title feat(nuxi): cli-wrapper for self restart feat(nuxi): cli wrapper for self restart Jan 31, 2023
@danielroe
Copy link
Member

This is lovely πŸ’―

@danielroe danielroe requested a review from pi0 January 31, 2023 13:52
Copy link
Member

@pi0 pi0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea to support restart, however, we can use the semi-standard signal SIGHUP to be handled for dev command and any other sub-command that needs to listen to it for restarting instead of restarting the entire CLI.

@Atinux
Copy link
Member

Atinux commented Jan 31, 2023

we can use the semi-standard signal SIGHUP to be handled for dev command

You mean listening in https://github.com/nuxt/nuxt/blob/main/packages/nuxi/src/commands/dev.ts ?

But the issue is that this file is already importing Nuxt dependencies where in the wrapper we don't have this caching issue

@pi0
Copy link
Member

pi0 commented Feb 1, 2023

But the issue is that this file is already importing Nuxt dependencies

That's also a huge performance gain to keep cache. What dependencies you are thinking would be cached and useful to be invalidated?

@danielroe
Copy link
Member

danielroe commented Feb 1, 2023

I think performance isn't the issue here; a full restart would be an exceptional situation and most likely in order to flush the cache, for example a programmatic nuxt/module version upgrade + restarting of nuxt.

@antfu
Copy link
Member Author

antfu commented Feb 3, 2023

The problem we are trying to solve here is the support for "Hard restart", where the cache is explicitly not desired (e.g. upgrade Nuxt itself). This shouldn't be a common operation as we already support "Soft restart" by config changes, etc. Performance is relevant here I think.

@antfu
Copy link
Member Author

antfu commented Feb 3, 2023

Oh, and I forgot to reference, Vitest has been using this pattern for months to capture Node crashes and handle the restart: https://github.com/vitest-dev/vitest/blob/4fc492c61d6e01bc34bc807a6b57197a1914f823/packages/vitest/src/node/cli-wrapper.ts#L134-L135

It works quite nicely and we don't see issues with this approach.

@danielroe danielroe added this to the v3.3 milestone Feb 16, 2023
Copy link
Member

@danielroe danielroe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. What do you think @pi0? We would then have both the soft restart in dev (from #19084) and also this 'hard' restart for exceptional circumstances.

@antfu
Copy link
Member Author

antfu commented Mar 1, 2023

By the discussion with @danielroe, I just pushed a commit to extend the restart hook to be:

await nuxt.hooks.callHook('restart', { hard: true })

Later, with the new CLI integration, we could keep the interface the same, making the transition invisible.

Copy link
Member

Atinux commented Mar 2, 2023

* }
* ```
*/
export const EXIT_CODE_RESTART = 44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I assume this implementation is temporary, code 44 is reserved for UNIX (Channel number out of range). As we don't (and shouldn't) use code 0, i think we can simply use that code as an indicator that child was killed and expects to be restarted (otherwise use a higher number)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, are you suggesting we use a different code than 44? 0 is the default, I think, and wouldn't indicate anything if we received it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was checking this table: https://tldp.org/LDP/abs/html/exitcodes.html says 3-125 are free to use.

And yes, 44 was a number I picked randomly.

Go a bit deeper I found this:
https://www.cyberciti.biz/faq/linux-bash-exit-status-set-exit-statusin-bash/
It seems to me the codes are specially for bash to exit, I suppose it shouldn't have much impact on our side (+ the code is only be captured by our wrapper)

So anyway I changed it to 85 as it's defined in the previous link as Interrupted system call should be restarted

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use 1. Auto restarting with child unwanted exit codes too as well as signaling explicitly (also consistent with sighup used for restart)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if I get what you mean - are you suggesting we exit with process.exit(1)? Wouldn't that be indistinguishable with the normal error exit?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A child normally doesn't exits with (1) but yeah it might introduce edge-cases.

Well, btw if we use fork, we can use the process communication channel to explicitly send a nuxt event (https://github.com/nuxt/cli/pull/22/files#diff-34d76a96faa930c4b9909c055beb422e601e2267f72ce12d6f15a1c2cc6ea914R57)

const cliEntry = fileURLToPath(new URL('../dist/cli-run.mjs', import.meta.url))

async function startSubprocess (preArgs: string[], postArgs: string[]) {
const child = await execa(
Copy link
Member

@pi0 pi0 Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf: I guess we can use fork (or exec) from nod:child_process. This is also consistent with the final nuxi-ng implementation and provides a communication channel.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we use execa in many places already, and the chunk is shared. I guess the performance won't be a big issue here.

I am just worried about using fork or exec might create more issues to handle on different OS, etc.

Copy link
Member

@pi0 pi0 Mar 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main usecase i added execa was search path compatibility which isn’t a case here. Also fork copies mem in unix then execs which by theory at least should be faster than loading node to the mem again.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we mesure the performance difference here?

I like the simplicity of code and execa has some good features:
CleanShot 2023-03-03 at 10 12 41@2x

Copy link
Member

@pi0 pi0 Mar 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of those benefits aren't relevant here and main benefit is using fork over exec with fine gained control. we are forking a known entry as process (not a on-off executation like other commands).

Finally this impl will be part of citty's dev server so this change is to make sure we are aligned :)

Copy link
Member

@pi0 pi0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea with introducing restart({ hard }) hook πŸ‘πŸΌ

Other than mentioned comments above, i think we can move forward with it unblocking features like #19394 (also adding hook types in next release!)

As I have drafted in nuxt/cli#22, the final new implementation of hard start can be completely different with the CLI server being alive across reloads. This means hard restart will have behavior differences and it might be internally aware of it.

@danielroe
Copy link
Member

I think we can go ahead and merge this and iterate on the implementation if needed. (It's not clear to me how much refactoring this is required given that we will shortly be migrating to nuxi-ng, but I'm not meaning to shut down discussion.)

@danielroe danielroe merged commit db5ea91 into main Mar 3, 2023
@danielroe danielroe deleted the feat/nuxi-restart branch March 3, 2023 13:45
@pi0
Copy link
Member

pi0 commented Mar 3, 2023

Please next times, wait on comments to be resolved before moving forward!

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

Successfully merging this pull request may close these issues.

None yet

4 participants