EE Counterpart: Puma in GDK and rack server lifecycle event abstractions
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22372
EE Counterpart forWhat is the purpose of this change
Part of https://gitlab.com/gitlab-org/gitlab-ce/issues/3592
Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/52762
This MR and its accompanying merge requests, is the first step forwards a multithreaded GitLab rails service.
Extends @yorickpeterse's WIP Puma MR, rebasing from master and including the start of GDK support https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1899
How does it do it
It allows the rails server to be started in puma. Since unicorn and puma both implement rack services, this isn't a particularly hard thing to do.
Most of the changes in this MR are related to the non-standard hooks that we use to be notified of unicorn lifecycle events, for example worker being started and stopped. These are not standardised and differ between unicorn, puma and other servers.
To make matters worse, much of this logic is spread out in Omnibus, which makes reasoning about it significantly more difficult. This is largely resolved in this MR - for example prometheus lifecycle code can now be found alongside prometheus initialisation code, instead of being in a different repository.
To resolve this, I've decoupled then lifecycle event sources from listeners with three types of cluster lifecycle events:
Event | Where | Puma Cluster | Unicorn | Omnibus Injects Code? | Omnibus Allows Injection? | LifecycleEvents |
---|---|---|---|---|---|---|
Before Fork | Master | before_fork |
before_fork |
Yes | Yes | before_fork |
After Fork | Worker | on_worker_boot |
after_fork |
Yes | Yes | worker_start |
Before Exec | Master | on_restart |
before_exec |
Yes | Yes | on_master_restart |
Now, for example, instead of handling events inside the unicorn before_fork
event, application code can register as follows:
Gitlab::Cluster::LifecycleEvents.on_worker_start do
# Start a thread....
end
This block will then get executed whenever a worker is forked: in other words, the block is executed once per process.
To invoke the events, LifecycleEvents
needs to be coupled to the server. This is implementation specific. For example in Puma this is done with:
before_fork do
Gitlab::Cluster::LifecycleEvents.signal_before_fork
end
on_worker_boot do
Gitlab::Cluster::LifecycleEvents.signal_worker_start
end
on_restart do
Gitlab::Cluster::LifecycleEvents.signal_master_restart
end
Whereas in Unicorn, this is done with a different set of methods:
before_exec do |server|
Gitlab::Cluster::LifecycleEvents.signal_master_restart
end
before_fork do |server, worker|
Gitlab::Cluster::LifecycleEvents.signal_before_fork
end
after_fork do |server, worker|
Gitlab::Cluster::LifecycleEvents.signal_worker_start
end
How to merge
-
The omnibus change needs to be merged concurrently to this MR: omnibus-gitlab!2783 (merged)
-
The EE change needs to be merged concurrently to this MR: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/7943
-
In order to enable this change in GDK, this GDK change needs to be merged after this is merged.
-
Note that GDK does not overwrite the
gitignore
dconfig/unicorn.rb
file but it's important that this file needs to be updated as part of this change. Any suggestions about how we going about doing this? One option would be to make a backup of the existing file and overwrite it, or possibly attempt to patch it? -
Run gdk with the
EXPERIMENTAL_PUMA
flag set: egEXPERIMENTAL_PUMA=1 gdk run
orEXPERIMENTAL_PUMA=1 gdk run app
etc