Worker is now the base class of all workers. It has a message
queue and can be run in a thread of its own, or in the calling
thread. Worker can not be used as such, but a concrete worker
class must be derived from it. Currently there is only one
concrete class RoutingWorker.
There is some overlapping in functionality between Worker and
RoutingWorker, as there is e.g. a need for broadcasting a
message to all routing workers, but not to other workers.
Currently other workers can not be created as the array for
holding the pointers to the workers is exactly as large as
there will be RoutingWorkers. That will be changed so that
the maximum number of threads is hardwired to some ridiculous
value such as 128. That's the first step in the path towards
a situation where the number of worker threads can be changed
at runtime.
A new class mxs::Worker will be introduced and mxs::RoutingWorker
will be inherited from that. mxs::Worker will basically only be a
thread with a message-loop.
Once available, all current non-worker threads (but the one
implicitly created by microhttpd) can be creating by inheriting
from that; in practice that means the housekeeping thread, all
monitor threads and possibly the logging thread.
The benefit of this arrangement is that there then will be a general
mechanism for cross thread communication without having to use any
shared data structures.
If maxadmin connections are handled by different workers, then
there may be a deadlock if some maxadmin command requires
communication with all workers.
Namely, in that case a message will be sent to all other workers
but the current one, but that message will not be handled if that
other worker at that point sits in the debugcmd_lock spinlock
in debugcmd.c:execute_cmd().
We can prevent that deadlock from happening simply by ensuring
that all maxadmin connections are handled by one thread.
The worker task should never be immediately executed to allow the task to
be executed on the next "tick" of the worker. This prevents recursive
calls to e.g. routeQuery in readwritesplit when errors are handled.
Readwritesplit would not handle multiple overlapping COM_STMT_EXECUTE
commands properly if they opened cursors. This was due to the fact that
the result would not be marked as complete and COM_STMT_FETCH commands
were executed as if they did not return results.
The correct implementation is to consider a COM_STMT_EXECUTE that opens a
cursor complete only when the first EOF packet is read (that is, when the
resultset header is read). This allows subsequent COM_STMT_FETCH commands
to be handled separately.
The separate COM_STMT_FETCH handling must count the number of packets that
are being fetched. This allows correct tracking of the state of a
COM_STMT_FETCH by checking that the number of packets is correct or the
second EOF/ERR packet is read.
Now that the readwritesplit uses the same mechanism for both
retry_failed_reads and delayed query retries, the re-routing function
should accept a delay of 0 seconds. This makes the mechanism more suitable
for other uses e.g. delaying of queries in filters.
The `error` variable was never used. Also added a more convenient typedef
for both the downstream and upstream functions and updated filter API
version.
The tasks themselves now control whether they are executed again. To
compare it to the old system, oneshot tasks now return `false` and
repeating tasks return `true`.
Letting the housekeeper remove the tasks makes the code simpler and
removes the possibility of the task being removed while it is being
executed. It does introduce a deadlock possibility if a housekeeper
function is called inside a housekeeper task.
The class now does all of the work and the API wraps the calls to the
member methods. Using an STL container makes the list management a lot
more convenient.
The old hkheartbeat variable was changed to the mxs_clock() function that
simply wraps an atomic load of the variable. This allows it to be
correctly read by MaxScale as well as opening up the possibility of
converting the value load to a relaxed memory order read.
Renamed the header and associated macros. Removed inclusion of the
heartbeat header from the housekeeper header and added it to the files
that were missing it.
This is a proof-of-concept that validates the query retrying method. The
actual implementation of the query retrying mechanism needs more thought
as using the housekeeper is not very efficient.
The code for figuring out the where to send a statement does no
longer depend upon RWSplitSession but only on QueryClassifier.
So now the functionality can be moved into QueryClassifier after
which further cleanup and streamlining can be done.
When the connection pool is inspected, both the client username and IP
must match. This causes the pool to be partitioned by username and IP,
prevening unintentional sharing of connections between different users.
This is the first step in providing a QueryClassifier class
that is capable of performing context dependent query classification,
where the context is essentially the session state.
The get_backend_from_dcb function needs to check that the backend is in
use before comparing the pointer. This prevents stale pointers from being
used and is logically more sound than relying on raw DCB matches.
The session command history is now compacted to contain only the first and
last execution of a session command. This should still allow most of the
more eccentric use-cases of user variables while keeping the session
command history smaller.
Added some convenience functions into the SessionCommand class to make the
pruning process easier.
Moved session command execution into the Backend class itself as the
session commands are defined as a related part of it. This allows all
connections to execute session commands if some are available.
Removed explicit SERVER_REF usage in the readwritesplit connection
creation code and replaced it with SRWBackend. This allows the removal of
the get_root_master_backend function which duplicated the functionality in
get_root_master.
The Backend::dcb() method gives the raw pointer to the internal DCB. This
pointer is used by at least readwritesplit to map raw DCB pointers to
backends. To prevent stale pointers from being returned, m_dcb needs to be
set to NULL after it has been closed.