If the startup of the listeners requires communication with all of the
workers, the workers must be up and running for that to happen.
Due to the fact that the main thread is still a worker thread, the
initialization code is not extra straightforward. By queuing an event to
the main worker, the startup of all listeners is done at a fully
operational state with all workers fully functional.
The service initialization code was also flawed in the sense that it would
cause a deadlock if any of the threads would have to check for the user
permissions. This is mainly a problem with the authenticator modules but
the benefits of the per service pre-loading of users is most likely
superficial. In theory startup will be faster as each thread now queries
the users in parallel.
By storing the reference in the DCB, the two-way dependency between the
listeners and services is severed. Now the services have no direct link to
listeners and after the destruction of a listener it will be freed once
all connections through it have closed.
Due to the fact that a listener itself has a DCB that must point to a
valid listener, a self-reference is stored in the listener DCB. This is
extremely confusing and is only here to keep the code functional until the
DCB part of the listener can be factored out.
By storing a shared pointer to the listeners in the services, they will be
available as long as the service using them exists. This enables clean
destruction of listeners that still have open sessions.
The listener creation code now separately creates the listener and links
it to the service. Also replaced relevant parts of the related code with
the listener implemented versions of it.
See script directory for method. The script to run in the top level
MaxScale directory is called maxscale-uncrustify.sh, which uses
another script, list-src, from the same directory (so you need to set
your PATH). The uncrustify version was 0.66.
The runtime modification of servers, services and monitors now validates
the parameters before starting the update process. This guarantees that
the set of parameters is valid before it is processed.
After the validation, the parameters are now also stored in the list of
configuration parameters. This will simplify the serialization process by
removing the need to explicitly serialize all common object parameters.
As the filters are only passed as a pipe separated list when the
configuration is being processed, there's no need to have the interface
conform to that. Passing a list of filter names makes it more flexible and
will make it's use in the runtime configuration easier.
The service now generates the configuration file that represents the
serialized form of it. Also removed the unused service_serialize_servers
function.
The services will now store a local copy of the filter lists in the worker
local data associated with the service. This removes the instance level
lock and removes the performance penalty that was previously imposed by
it.
The only remaining performance "regression" compared to 2.2 is the extra
two atomic operations per filter that a session does when it is being
created. This is quite hard to get rid of without significant amounts code
and will hopefully be just a drop in the ocean.
The most relevant string variables of a service are now duplicated as C++
strings. This should ease the eventual transition to a fully C++ internal
representation of the service. The array of refresh rates was also wrapped
inside a std::vector to remove the need to manually manage memory.
Separated the SERVICE_USER struct into its individual components as there
was no real need to have them inside a struct.
The filters can now be altered at runtime. Currently, the filter usage
uses a service level lock. The next is to allocate a per-service key to
the worker local data and use that to orchestrate the storage of the
filter lists.
Removed the explicit setters for the service parameters. Not all of them
were implemented and they were only used internally. Moved the parameter
validation and update processing inside the Service class to reduce the
load on the other parts of the core.
The service now has a private std::mutex that is used for
synchronization.
Renamed the vector of services to use snake_case.
Use lock guards with mutexes to make usage easier and safer. This makes
the code smaller as well as slightly easier to read.
The service now uses a std::vector<SFilterDef> to store the filters it
uses. Most internal parts deal with the SFilterDef but debugcmd.cc still
moves raw pointers around (needs to be changed).
The service would not be in the list if it failed before it was placed
there. Moving the actual freeing of memory into the Service destructor
allows it to be called directly when we know the service is not in the
list. This also only allows valid services to be placed into the global
list of services.
To prevent freeing a partially constructed service, the memory allocation
checks were replaced with a runtime assertion. This can be changed when
the creation of the service is done only at a point where we know it can't
fail. Currently, the createInstance call expects the service as a
parameter which prevents this.
Replaced the previous RESULTSET with the new implementation. As the new
ResultSet doesn't have a JSON streaming capability, the MaxInfo JSON
interface has been removed. This should not be a big problem as the REST
API offers the same information in a more secure and structured way.
Converted the internal service header to a C++ header and moved all
functions there that are for internal use only.
Added the new Service type that inherits the SERVICE struct. This is to
distinct the opaque external C interface from the C++ internals.