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

Feature request: add a "on process start" hook? #535

Closed
travisbell opened this issue Mar 5, 2021 · 8 comments
Closed

Feature request: add a "on process start" hook? #535

travisbell opened this issue Mar 5, 2021 · 8 comments
Labels
z-enhancement ⬆️ Product Enhancement

Comments

@travisbell
Copy link

travisbell commented Mar 5, 2021

Hey guys,

I've been working through getting a few Ruby apps of mine battle tested with NGINX Unit and was wondering if there's a chance you would consider adding some hooks around starting up new processes.

The background here is that (in the Ruby world at least), most app servers have some hooks like this. Puma, Unicorn and Iodine for example, provide some ways to execute code on each worker after they've been started. This can be useful to handle things like warming the workers up by connecting to the database and/or caches.

The most popular app server in Ruby, Puma, has a on_worker_boot hook. It would be really nice to see something like this for Unit, and I hope you'll consider it.

@VBart
Copy link
Contributor

VBart commented Mar 5, 2021

Note that Unit supports multithreading as well. Do you need a hook to be called on each thread or just per process?

@VBart VBart added the z-enhancement ⬆️ Product Enhancement label Mar 5, 2021
@travisbell
Copy link
Author

Ideally, both.

Here's the hooks Puma supports. Not all of these are going to be applicable to Unit, but you can see the bigger picture:

before_fork
    # Code to run immediately before master process
    # forks workers (once on boot). These hooks can block if necessary
    # to wait for background operations unknown to Puma to finish before
    # the process terminates.
    # This can be used to close any connections to remote servers (database,
    # Redis, ...) that were opened when preloading the code.

on_worker_boot
    # Code to run in a worker when it boots to setup
    # the process before booting the app.

on_worker_shutdown
    # Code to run immediately before a worker shuts
    # down (after it has finished processing HTTP requests). These hooks
    # can block if necessary to wait for background operations unknown
    # to Puma to finish before the process terminates.

on_worker_fork
    # Code to run in the master right before a worker is started. The worker's
    # index is passed as an argument.

after_worker_fork
    # Code to run in the master after a worker has been started. The worker's
    # index is passed as an argument.

Thanks Valentin.

@ocanty
Copy link
Contributor

ocanty commented May 20, 2021

Hi @travisbell,

We're currently working on this feature, if you would like to evaluate a patch that is currently being worked on and see if it meets your needs:

https://gist.github.com/ocanty/66a820989d98568b73201a2fc4565b76

An example of configuration with the new "hooks" attribute:

{
    "type": "ruby",
    "processes": 2,
    "threads": 2,
    "user": "vagrant",
    "group": "vagrant",
    "script": "config.ru",
    "hooks": "hooks.rb",
    "working_directory": "/home/vagrant/unit/rbhooks",
    "environment": {
        "GEM_HOME": "/home/vagrant/.ruby"
    }
}

An example of a valid "hooks.rb" file follows:

File.write("./hooks.#{Process.pid}", "hooks evaluated")

on_worker_boot do
    File.write("./worker_boot.#{Process.pid}", "worker booted")
end

on_thread_boot do
    File.write("./thread_boot.#{Process.pid}.#{Thread.current.object_id}",
               "thread booted")
end

on_thread_shutdown do
    File.write("./thread_shutdown.#{Process.pid}.#{Thread.current.object_id}",
               "thread shutdown")
end

on_worker_shutdown do
    File.write("./worker_shutdown.#{Process.pid}", "worker shutdown")
end

@travisbell
Copy link
Author

travisbell commented May 20, 2021

This is fantastic! I'll try to find some time to apply the patch and experiment.

nginx-hg-mirror pushed a commit that referenced this issue Jul 2, 2021
This feature allows one to specify blocks of code that are called when certain
lifecycle events occur.  A user configures a "hooks" property on the app
configuration that points to a script.  This script will be evaluated on boot
and should contain blocks of code that will be called on specific events.

An example of configuration:

{
    "type": "ruby",
    "processes": 2,
    "threads": 2,
    "user": "vagrant",
    "group": "vagrant",
    "script": "config.ru",
    "hooks": "hooks.rb",
    "working_directory": "/home/vagrant/unit/rbhooks",
    "environment": {
        "GEM_HOME": "/home/vagrant/.ruby"
    }
}

An example of a valid "hooks.rb" file follows:

File.write("./hooks.#{Process.pid}", "hooks evaluated")

on_worker_boot do
    File.write("./worker_boot.#{Process.pid}", "worker booted")
end

on_thread_boot do
    File.write("./thread_boot.#{Process.pid}.#{Thread.current.object_id}",
               "thread booted")
end

on_thread_shutdown do
    File.write("./thread_shutdown.#{Process.pid}.#{Thread.current.object_id}",
               "thread shutdown")
end

on_worker_shutdown do
    File.write("./worker_shutdown.#{Process.pid}", "worker shutdown")
end

This closes issue #535 on GitHub.
@travisbell
Copy link
Author

travisbell commented Aug 20, 2021

I see this was included in 1.25.0, and it's working great. Thanks for the addition!

@ghost ghost moved this from To Do to Released in NGINX Unit roadmap-tracker Nov 23, 2021
@krburkhart
Copy link

This is very close to what I was looking for in #581, is there any chance of implementing such a thing for python?

@VBart
Copy link
Contributor

VBart commented Nov 24, 2021

@krburkhart but it's already implemented as lifespan.shutdown. The shutdown hooks in Ruby work the same.

@krburkhart
Copy link

My issue with that is lifespan.shutdown doesn't fire until after web socket connections have closed. But they won't ever close because they are long-running connections. If unit lifespan worked like uvicorn and closed web socket connections when the listener is removed it would make things easier for me. I realize that this is inconsistent with http connections; I want the process to stay alive until those are closed.

I have a workaround (utilizing the restart control function), but it involves a fair amount of code on my side that I worry is a bit fragile.

Sorry to re-hash this here, I did not realize this is the same as lifespan.

@tippexs tippexs moved this from 🚀 Released to 🚀 Released1 in NGINX Unit roadmap-tracker Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
z-enhancement ⬆️ Product Enhancement
Projects
Status: 🚀 Released
NGINX Unit roadmap-tracker
  
🚀 Released
Development

No branches or pull requests

4 participants