WL#6128: Session Tracker: Add GTIDs context to the OK packet
Affects: Server-5.7
—
Status: Complete
MOTIVATION ========== As part of the framework to implement several degrees of session consistency over a farm of MySQL servers, one needs to put in the OK packet session state. This information must provide input for that the connector (or middleware) to act and make sure it provides the correct consistency level to the application, when routing queries to different servers. For example, putting GTIDs in the OK packet may help the connector to track dependencies between transactions and state database transitions, by comparing GTID state on different servers and the GTID information it holds. OBJECTIVE ========= Therefore, this worklog adds a tracker, to the response packet of the MySQL protocol, to be able to pass to the connector information about the session state collected that shall be used to implement session consistency. In this worklog, we will be taking, packing and sending the data provided by WL#6972, i.e., a set of GTIDs. SCOPE ===== This worklog implements only the tracker. The part that changes the server and replication layer to collect the necessary data is designed and implemented on WL#6972.
F1. If session_track_gtids is set to a value other than 'OFF' then the
OK packet MUST include the collected data.
F2. The tracker will consider a state change whenever a GTID is
collected.
F3. The GTIDs representation MUST be extensible in the future without
breaking the connectors w.r.t. the structure of the information
exchanged. Therefore, an encoding specification field must exist
in the payload of the OK packet, to give the connector/decoder the
ability to decide whether it is able to decode the payload or not.
WL#4797: Extending protocol's OK packet
WL#6972: Collect GTIDs to include in the protocol's OK packet
WL#6972: Collect GTIDs to include in the protocol's OK packet
INTRODUCTION ============ To implement different degrees of session consistency over a set of mysql servers, the connector or the middleware software needs to track the state of different servers in the distributed system. The mechanism under development will collect data after a transaction commits (the current design considers a set of GTIDs - see WL#6972). The data is extracted from the server and from the replication layer. Then, it is put in the OK packet allowing the connector to track distributed state. In the current design, the data provided to the tracker is a set of GTIDs. GTIDs allow tracking dependencies between transactions and database state transitions. However, it could be other type of information provided that it could be used to track dependencies among changes, for instance, an "epoch" from cluster. Getting back to GTIDs, knowing which transactions have been applied where, makes it easier for the connector to compare states and identify dependencies among transactions. For instance (and very roughly), consider that the user has configured the connector to make sure that whatever read is issued, it will always include the latest write that was issued before by him (even if the connector decides that the read operation is to be done on a slave). Resorting to GTIDs, the connector can compare implicitly the slave's state against the master's state and defer the read until all meaningful writes, coming from the master, have been replayed on the slave. The following diagram illustrates in detail a possible sequence. ,-----------. ,---------. ,------. ,-----. |Application| |Connector| |Master| |Slave| `-----+-----' `----+----' `--+---' `--+--' | INSERT... | | | |-------------------->| | | | | | | | | INSERT... | | | |----------------->| | | | | | | | (OK, gtids) | | | |<- - - - - - - - -| | | | | | | OK | | | |<- - - - - - - - - - | | | | | | | | SELECT... | | | |-------------------->| | | | | | | | | GTID_WAIT(gtids) | | |----------------------------------->| | | | | | | SELECT... | | |----------------------------------->| | | | | | | (OK, gtids) | | |<- - - - - - - - - - - - - - - - - -| | | | | | OK | | | |<- - - - - - - - - - | | | ,-----+-----. ,----+----. ,--+---. ,--+--. |Application| |Connector| |Master| |Slave| `-----------' `---------' `------' `-----' Note that even though it is possible to query the server for the "last committed global transaction ID", this introduces an extra roundtrip, which introduces additional latency, potentially leading to performance degradation. PROBLEM STATEMENT ================= To be able to correctly track the gtid state, the necessary data must be returned back to the connector after each transaction commits, in the OK packet. Since this work is handling the data collected according to WL#6972 spec, this means that the data inserted into the OK packet is a GTID set. Which elements are in this set is very dependent on the consistency level. The information that is put into the OK packet is controled by the session variable introduced in WL#6972, which is named session_track_gtids. This is relevant to this worklog, since it also controls whether the tracker is enabled or not. The main problem is then how to serialize and put into the OK packet the data to be returned. This is the focus of this worklog. SOLUTION ======== The solution to the problem mentioned is to implement a new State_tracker, as defined in WL#4797. This state tracker will collect the data provided by WL#6972 (a GTID set) and add it to the OK packet. encoding -------- The encoding will be done as a plain string. In the future we may decide to encode on a different format (e.g., json). optimizations ------------- There will not be any optimization regarding keeping and/or encoding partial state in this version. In the future, this may be an optimization that one can do. Open-Ended Encoding ------------------- Given that we may want to encode using different formats or even do some optimizations to the way connectors and the server keep state of which GTIDs have been exchanged, we will reserve a field in the payload to be able to state which encoding specification this GTIDs state relates to. In the future, the connector may check this field and decide which unpacking mechanism it should use if it chooses to decode the GTID set in the OK packet. This opens room for implementing a configuration option that governs the encoding of the GTID payload in the tracker, e.g., SET @@session_track_gtids_encoding_spec='JSON'; SET @@session_track_gtids_encoding_spec='DEFAULT'; SET @@session_track_gtids_encoding_spec='PLAIN | DELTA'; etc. This worklog implements only one type of encoding, thus there is no point adding the variable right away. If need be - for instance, we add some more encoding types - we can implement this as part of another worklog. Payload (ABI change) -------------------- As such, considering the above, the payload regarding the information serialized by the tracker into the OK packet is: +-----------------------------------------------------------------------+ | tracker type | entity len | encoding spec | gtids len | gtids | | (1-9 bytes) | (1-9 bytes) | (1-9 bytes) | (1-9 bytes) | (N bytes)| +-----------------------------------------------------------------------+ Given that there is only one encoder, the default encoding spec is set to be number 0 and as such it shall be encoded using only one byte. API Change ---------- The list of trackers in mysql_com.h interface needs to be extended. We added SESSION_TRACK_GTIDS to the list of session state types: enum enum_session_state_type { SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */ SESSION_TRACK_SCHEMA, /* Current schema */ SESSION_TRACK_STATE_CHANGE, /* track session state changes */ SESSION_TRACK_GTIDS /* track gtids */ }; #define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES #define SESSION_TRACK_END SESSION_TRACK_GTIDS
TASKS
-----
The work needed to implement this worklog may be split into the
following subtasks:
A. Create a new State_tracker for tracking GTIDS:
class Session_gtids_ctx_encoder;
class Session_gtids_tracker : public State_tracker,
Session_gtids_ctx::Ctx_change_listener
{
private:
Session_gtids_ctx_encoder *encoder;
(...)
public:
bool store(THD* thd, String& buf) { return encoder->encode(thd, buf); }
(...)
}
This class will listen for changes in the GTIDs context and will
hold a reference to an instance of an encoder.
We shall not have instances of encoders when they are not
needed. As such, the lifetyime of an instance depends on the value
assigned to SESSION_TRACK_GTIDS.
B. Create an interface for the gtid set encoder.
class Session_gtids_ctx_encoder
{
public:
ulonglong encoding_specification()= 0;
bool encode(THD *thd, String& buf)= 0;
}
This is an abstract class that defines the interface for the
encoder.
C. Create an implementation of the encoder interface.
class Session_gtids_ctx_encoder_string : public Session_gtids_ctx_encoder
{
public:
ulonglong encoding_specification() { return 0; }
bool encode(THD *thd, String& buf)
{
/* encoding implementation goes here */
(...)
}
}
This is the default encoder implementation. Different encoders
shall extend either the abstract class or a specialized class and
implement their own encode member function. The long integer
returned by the encoding_specification member function MUST be
unique.
D. Define a listener interface. This interface must be implemented by
the tracker to listen to Session_gtids_ctx changes.
class Session_gtids_ctx
{
public:
class Ctx_change_listener
{
public:
Ctx_change_listener() {}
virtual void notify_session_gtids_ctx_change()= 0;
private:
// not implemented
Ctx_change_listener(const Ctx_change_listener& rsc);
Ctx_change_listener& operator=(const Ctx_change_listener& rsc);
};
}
The Session_gtids_tracker must extend Session_gtids_ctx::Ctx_change_listener
and implement the member function:
void notify_session_gtids_ctx_change();
The implementation will then call the State_tracker interface function
void mark_as_changed(LEX_CSTRING *name);
By extending Session_gtids_ctx, by implementing
Ctx_change_listener::notify_session_gtids_ctx_change and by registering
on Session_gtids_ctx the link between the GTIDs collector and the
tracker + encoder is established and there is no cross-dependency
between both.
E. Extend the Session_gtids_ctx with member functions to register a
listener.
The class Session_gtids_ctx interface must be extended to hold two
new member functions definitions:
/**
Registers the listener. The pointer MUST not be NULL.
@param listener a pointer to the listener to register.
*/
void
register_ctx_change_listener(Session_gtids_ctx::Ctx_change_listener*
listener);
/**
Unregisters the listener. The listener MUST have registered previously.
@param listener a pointer to the listener to register.
*/
void
unregister_ctx_change_listener(Session_gtids_ctx::Ctx_change_listener*
listener);
NOTES:
- The act of collecting GTIDs happens only if there are listeners
registered.
- The internal structures Gtid_set and Sid_map lifetime is governed
by whether there is a listener registered or not. The reason
being, because we do not want to use memory unnecessarily.
F. Extend the list of session trackers in the mysql_com.h interface:
enum enum_session_state_type
{
SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */
SESSION_TRACK_SCHEMA, /* Current schema */
SESSION_TRACK_STATE_CHANGE, /* track session state changes */
SESSION_TRACK_GTIDS /* track gtids */
};
#define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES
#define SESSION_TRACK_END SESSION_TRACK_GTIDS
G. Add support in libmysql to read the data from the new
SESSION_TRACK_GTIDS, in "void read_ok_ex(MYSQL *mysql, ulong length)".
FOR TESTING PURPOSES:
H. Change client/mysqltest.cc to support the new SESSION_TRACK_GTIDS
tracker type.
Copyright (c) 2000, 2025, Oracle Corporation and/or its affiliates. All rights reserved.