WL#13707: add systemd notify support

Affects: Server-8.0   —   Status: Complete

Motivation

MySQL Router currently interacts with systemd through pid-file and exit-codes.

systemd allow more detailed status information to be reported back like:

  • READY=1 to report a service is ready to handle connections
  • RELOADING=1 in case config is reread and service is currently not ready
  • STOPPING=1 in case if shutting down

Futher reading: http://0pointer.de/public/systemd-man/sd_notify.html

R1
If there is a NOTIFY_SOCKET environment variable set when the Router starts, the Router MUST treat the value of this variable as the name of the unix-domain socket, DGRAM on all unix-based OSes
R2
On Windows it MUST treat the NOTIFY_SOCKET as a named pipe path.
R3
If that NOTIFY_SOCKET is set the Router MUST write "READY=1" to the platform specific IPC channel pointed by it when all its components are fully initialized.
R4
The Router MUST declare that it is ready when all the plugin instances that are provisioned in the current running configuration are ready. Some of the plugins are only a helper plugins that do not expose any user-visible readiness (like logger, http_auth_backend etc). The others need to explicitly declare that they are fully initialized and ready to provide their service.
R4.1
Additionally on the Unix-based OSes also the interrupt handler has to be set up in order for the Router to declare it is ready.
R5
If there is no plugin that declares the readiness configured the Router itself MUST declare that it is ready at the initialization.
R6
When the NOTIFY_SOCKET environment variable is not set or empty the Router MUST skip the step of declaring it is ready and continue working as before introducing this WL.
R7
The Router MUST attempt writing to the notify socket only once, when the last of the configured plugins becomes ready. If that fails, a warning MUST get logged and the Router MUST continue working as before introducing this WL.
R8
If there is a NOTIFY_SOCKET environment variable set when the Router is shutting down, the Router should write the "STOPPING=1" to the IPC channel pointed by the NOTIFY_SOCKET
R9
The systemd support should be integrated with the packaging scripts for the OSes that support it (deb, rpm)
R10
Router should not send no READY nor STOPPING notifications when it is being ran for the bootstrap (bootstrap mode)

Plugin is ready

There are currently server plugins that provides some service, either to the Router user or to the other Router plugins. These should notify when their service is ready.
  • the routing plugin instance (one for each configured routing section) is ready when it set up the network endpoint (whether it is TCP or unix socket) and it is ready to accept the client connections on that endpoint.
  • the metadata_cache plugin is ready when it connected to one of the configured metadata servers and successfully fetched the Cluster metadata.
  • the http_server plugin is ready when it set up the network endpoint and is ready to serve the http requests.
  • the rest_api, rest_metadata_cache, rest_router and rest_routing plugins are ready when they successfully added the relevant html paths to the http_server

Implementation

For the Router to be able to collect the readiness of all the configured plugin instances the Plugin struct is extended with additional boolean field.


struct Plugin {
  //..
  bool declares_readiness;
}

If the given plugin sets true in that field that means it is required to call the mysql_harness::on_plugin_ready(); whenever it initialized and ready to serve it's purpose. Usually that should be done somewhere in the start() once the service has started and the plugin enters the service loop.

That lets the Loader to know how many plugin instances there are configured that have to declare their readiness before the Router can declare it is ready itself. Before calling the start_all() the Router counts the number of those plugins and sets a global variable num_of_non_ready_plugin_instances to that number.

Each plugin that has declares_readiness=true at some point, when it is fully initialized, calls:


void on_plugin_ready(const std::string &name) {
  log_debug("Plugin %s ready", name.c_str());
  if (--num_of_non_ready_plugin_instances == 0) {
    log_debug("Service ready!");
    notify_ready();
  }
}