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
Keep plugins persistently running in the background #12064
Conversation
This is very cool! Excited to play with it. I'm wondering if there should be some type of flag or mode that means "run once and exit" vs "keep running in the background". Not all plugins need to hang around. Some just need to process their data and exit. On one hand I think about |
@fdncred My current thinking on this is to make it user-configurable. So, for example, you might have plugin-specific config of when plugins should be stopped if they haven't been used in long enough: $env.config.plugin_gc = {
default: {
enabled: true
remove_after: 10sec
}
plugins: {
dataframe: {
enabled: false
}
slow_python_thing: {
remove_after: 5min
}
}
} The one thing I'm not quite sure about with this is whether and how a plugin should communicate to the engine that it doesn't want to be stopped automatically, even if the time is up. There are a few things I've thought about:
I kind of like the last one personally. Not all custom values are going to be handles, some will be perfectly fine if the plugin is restarted because they contain all of the data they need |
Just shooting from the hip, but I could see 2 or 4 working pretty well. (i enumerated your bullets for easier discussion). I kind of lean towards 2 because it gives plugins the power to say, "keep me aliver", or, "ok, i'm ready to close". |
For another idea that is dataframe-like, I've always wanted to use this in nushell somehow https://github.com/vincev/dply-rs. |
That's cool, though I think |
I think I'll incorporate GC into this as there are probably users who won't like this feature very much if we don't have it. @fdncred you've convinced me to go with (2) I think, with just an explicit on/off control. It's a little more work for a plugin developer if they're doing something obvious like giving out handles in custom values, but they're probably tracking those all somewhere anyway - they just need to then make sure that when they tell the engine to turn that on/off, they're synchronizing it so they don't end up with a race condition where thread A is in the condition to turn it off, thread B is in the condition to keep it on, and then the off message gets sent without turning it back on again. But it does have the most flexibility for plugin developers to decide what makes the most sense for them - if it's something that really just always needs to be persistent, they can just turn GC off when they run a command for the first time and leave it that way. Outside of that though I will definitely make it user configurable, as there will probably be different feelings about GC even on a per-plugin basis. |
Wow! This is very cool.
Would a heartbeat system make sense? i.e. plugins would have to tell Nu "keep me alive" every X seconds to avoid being killed. |
Hmm... it wouldn't really fit the current API very well, because usually the only way you get to tell the engine anything is from within some kind of call of the plugin; you don't generally just get to communicate with the engine whenever you want. But that's not impossible to support by any means - it can be done. I think what I don't really love about it though is that it would require plugins to basically all do the same thing, spawning a thread just to deliver heartbeats every so often, reading some kind of internal state to decide whether to stop sending heartbeats. It would make more sense in a network context, where you don't always really know for sure if the other side is gone unless you send something and expect a reply - but this over stdio, and if a plugin crashes, we should know immediately. It's also not really a good indication of whether the plugin is hung and not responding to input, as a thread that's just sending out heartbeats won't necessarily be reflective of that. I think it's better to keep it simple. Worst case, if a plugin is keeping itself alive and the user doesn't want that (or thinks the plugin is messed up), there is the |
I believe adding the GC feature with plugins being able to opt out of it will require #12029, because plugins don't currently get |
Sorry I have a question: is there a case that a plugin needs to tell nushell engine that it doesn't want to stop? I think setting |
Yes: there are some plugins that won't work correctly with the default settings otherwise, since it should be on by default. For example, for dataframes, they'd basically just disappear a short time later. The plugin author would have to let their users know to disable the GC, and they'd probably still get a lot of complaints about it. It isn't really a concern that an evil plugin would be created - the user can always explicitly |
I was (am?) a little unclear on the intended plugin lifecycle after this PR. Is the following correct?
|
This is correct - plugins belong to a specific Nu invocation, and if you launch Nu from Nu, if that Nu uses plugins it will have to launch its own copies of them.
Yes, kind of. OS process management like Anyway, plugins that are behaving properly will always exit even if Nushell crashes. There isn't anything that Nushell has to explicitly send to the plugins to make them stop. |
Taking this back to draft while I work on the plugin GC feature |
Okay, I'm happy with the plugin GC feature now. See the edited description above. |
@devyn as predicted, conflicts. LOL. I think we can land this once they're resolved. |
This PR uses the new plugin protocol to intelligently keep plugin processes running in the background for further plugin calls. Running plugins can be seen by running the new `plugin list` command, and stopped by running the new `plugin stop` command. This is an enhancement for the performance of plugins, as starting new plugin processes has overhead, especially for plugins in languages that take a significant amount of time on startup. It also enables plugins that have persistent state between commands, making the migration of features like dataframes and `stor` to plugins possible. The `version` command now lists plugin names rather than plugin commands. The list of plugin commands is accessible via `plugin list`.
@fdncred all green now! I also improved the handling of critical errors (such as version mismatch, protocol error, unexpected exit, etc.) for plugins so that
|
oh boy, here we go! thanks again! |
@devyn ugh, can't compile now.
|
FYI - adding |
I've tried to do |
Looks to be Detegr/rust-ctrlc#115 |
hopefully the ctrlc people are responsive and fix it quick. i don't like having nushell main compiling problems. at least --locked still works. |
…gin' feature not enabled (#12144) # Description There is a warning about unused `IntoSpanned` currently when running `cargo check -p nu-parser`, introduced accidentally by #12064. This fixes that. # User-Facing Changes None # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib`
|
Description
This PR uses the new plugin protocol to intelligently keep plugin processes running in the background for further plugin calls.
Running plugins can be seen by running the new
plugin list
command, and stopped by running the newplugin stop
command.This is an enhancement for the performance of plugins, as starting new plugin processes has overhead, especially for plugins in languages that take a significant amount of time on startup. It also enables plugins that have persistent state between commands, making the migration of features like dataframes and
stor
to plugins possible.Plugins are automatically stopped by the new plugin garbage collector, configurable with
$env.config.plugin_gc
:If garbage collection is enabled, plugins will be stopped after
stop_after
passes after they were last active. Plugins are counted as inactive if they have no running plugin calls. Reading the stream from the response of a plugin call is still considered to be activity, but if a plugin holds on to a stream but the call ends without an active streaming response, it is not counted as active even if it is reading it. Plugins can explicitly disable the GC as appropriate withengine.set_gc_disabled(true)
.The
version
command now lists plugin names rather than plugin commands. The list of plugin commands is accessible viaplugin list
.Recommend doing this together with #12029, because it will likely force plugin developers to do the right thing with mutability and lead to less unexpected behavior when running plugins nested / in parallel.
User-Facing Changes
plugin list
plugin stop
version
(now lists plugin names, rather than commands)$env.config.plugin_gc
inc
did might misbehave until fixed&EngineInterface
so that the GC disable feature works. Add support for engine calls from plugins #12029 does this anyway, and I'm expecting (resolvable) conflicts with thatTests + Formatting
toolkit fmt
toolkit clippy
toolkit test
toolkit test stdlib
Because there is some specific OS behavior required for plugins to not respond to Ctrl-C directly, I've developed against and tested on both Linux and Windows to ensure that works properly.
After Submitting
I think this probably needs to be in the book somewhere