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, 2024, Oracle Corporation and/or its affiliates. All rights reserved.