WL#11381: Add asynchronous support into the mysql protocol
Affects: Server-8.0
—
Status: Complete
C APIs are synchronous, which means client sends a request and waits until server responds. This makes applications to not do anything until server sends a response. There can be a requirement where client is not bothered about when server sends data back, client can submit a query, do some other tasks and when in need of server response, client can check if server has sent the response and act accordingly. Thus making a need to implement asynchronous communication between client and server. use case: There can be a PHP script which can submit multiple queries from multiple connections and return immediately without waiting for the results. Later it can poll on results from all the connections to get the results and do further processing on fetched data.
FR1. All C APIs which read data from the socket will have an equivalent
non blocking version of C API.
FR2. These C APIs include following:
mysql_real_connect, mysql_send_query, mysql_real_query, mysql_store_result
mysql_next_result, mysql_fetch_row, mysql_free_result.
FR3. New non blocking version of above C APIs are as follows:
mysql_real_connect_nonblocking, mysql_send_query_nonblocking,
mysql_real_query_nonblocking, mysql_store_result_nonblocking,
mysql_next_result_nonblocking, mysql_fetch_row_nonblocking,
mysql_free_result_nonblocking
FR4. Non blocking version of C APIs does not wait for data to be available on the
socket to be read. These APIs will return immediately with a return status.
FR5. Return status is used to decide if the API has completed its operation or
needs to be called again at some later point in time to complete the
operation.
FR6. APIs which will not be asynchronous are as follows:
mysql_autocommit, mysql_change_user, mysql_close, mysql_commit,
mysql_connect, mysql_dump_debug_info, mysql_kill, mysql_list_*
mysql_ping, mysql_query, mysql_refresh, mysql_reload,
mysql_reset_connection, mysql_rollback, mysql_select_db,
mysql_set_server_option, mysql_shutdown, mysql_stat, mysql_use_result,
mysql_stmt_*, mysql_binlog_*
In mysql, client server communication is synchronous which means client sends
a request and waits in an idle state until it receives a response from server.
This idle state can be overcome by making the client server communication
asynchronous. Thus in an asynchronous communication protocol, client sends a
request which makes the request to return immediately without waiting for
server response and let client do some other tasks. When client at some later
point in time needs the server response can further read the network to get
the data. To achieve this kind of communication this WL will introduce a list of
non blocking C APIs.
Below is the list of C APIs which wait on the network socket for data to be
available for further processing:
mysql_autocommit
mysql_change_user
mysql_close
mysql_commit
mysql_connect
mysql_dump_debug_info
mysql_kill
mysql_list_dbs
mysql_list_fields
mysql_list_processes
mysql_list_tables
mysql_ping
mysql_query
mysql_real_connect
mysql_refresh
mysql_reload
mysql_reset_connection
mysql_rollback
mysql_select_db
mysql_set_server_option
mysql_shutdown
mysql_stat
mysql_use_result
mysql_stmt_close
mysql_stmt_execute
mysql_stmt_fetch
mysql_stmt_free_result
mysql_stmt_next_result
mysql_stmt_prepare
mysql_stmt_reset
mysql_stmt_send_long_data
mysql_stmt_store_result
mysql_binlog_open
mysql_binlog_fetch
All the above C APIs call these below C APIs:
1. mysql_send_query
2. mysql_real_query
3. mysql_store_result
4. mysql_next_result
5. mysql_fetch_row
6. mysql_free_result
These C APIs are the once which futher make calls to read or write to the
network socket. Thus by introducing non blocking version of C APIs for
these 6 APIs will make client have asynchronous communication support.
Connection phase between client server is also synchronous. To make this
asynchronous non blocking version of mysql_real_connect will be introduced.
This WL will have these non blocking C APIs:
mysql_real_connect_nonblocking
mysql_send_query_nonblocking
mysql_real_query_nonblocking
mysql_store_result_nonblocking
mysql_next_result_nonblocking
mysql_fetch_row_nonblocking
mysql_free_result_nonblocking
Function signature for above non blocking C APIs is as below:
enum net_async_status
mysql_real_connect_nonblocking(MYSQL *mysql,
const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port,
const char *unix_socket,
unsigned long clientflag);
Description:
This API attempts to initialize all the context needed to make an asynchronous
connection followed by establishing a connection to MySQL database. If this
API returns NET_ASYNC_COMPLETE then connection is established else call this
API from the client application until the status returned is NET_ASYNC_COMPLETE.
Parameter description:
The parameters are same as specified for mysql_real_connect()
enum net_async_status
mysql_real_query_nonblocking(MYSQL *mysql,
const char *query,
unsigned long length);
Description:
Executes the SQL statement pointed by query. This sql statement length is set in
length parameter. query string can contain multiple sql statements separated by
semicolons. This function can return immediately with status set to
NET_ASYNC_NOT_READY, in this case client application is expected to call this
API until it returns NET_ASYNC_COMPLETE.
Parameter description:
First parameter is address of MYSQL structure.
Second parameter specifies a string which holds sql statements.
Third parameter specifies length of query string.
enum net_async_status
mysql_send_query_nonblocking(MYSQL *mysql,
const char *query,
unsigned long length);
Description:
Executes the SQL statement pointed by query. This API is called by
mysql_real_query_nonblocking to send query to server in asynchronous way.
Parameter description:
Same as for mysql_real_query_nonblocking.
enum net_async_status
mysql_store_result_nonblocking(MYSQL *mysql,
MYSQL_RES **result);
Description:
Same as mysql_store_result expect that this API can return immediately without
reading all results from server.
Parameter description:
First parameter is address of MYSQL structure.
Second parameter is MYSQL_RES result structure with the results. MYSQL_RES
value NULL can represents either empty resultset or an error. If it is an
error then end user should call mysql_error() to check the same. If there is
no error it means there is no result sets to be read.
enum net_async_status
mysql_next_result_nonblocking(MYSQL *mysql);
Description:
Same as mysql_next_result expect that this API can return immediately without
reading next statement result.
Parameter description:
First parameter is address of MYSQL structure.
enum net_async_status
mysql_fetch_row_nonblocking(MYSQL_RES *res,
MYSQL_ROW *row);
Description:
Reads next row of a result set in an asynchronous way.
Parameter description:
First parameter is MYSQL_RES structure with the results.
Second parameter specifies the read row. NULL if there are no more rows to
retrieve or if an error occurred. When used after mysql_use_result(), NULL
indicates that there are no more rows to retrieve or if an error occurred. To
determine whether an error occurred, check whether mysql_error() returns a
nonempty string or mysql_errno() returns nonzero.
enum net_async_status
mysql_free_result_nonblocking(MYSQL_RES *result);
Description:
Frees the memory allocated for a result, set by APIs which would have returned
rows.
Parameter description:
First parameter is MYSQL_RES structure with the results.
For all above non blocking C APIs whose return type is net_async_status, return
value description is below:
NET_ASYNC_COMPLETE if asynchronous operation is complete.
NET_ASYNC_NOT_READY if operation is incomplete. In this case call API again
until NET_ASYNC_COMPLETE is returned.
NET_ASYNC_ERROR if asynchronous operation ended with an error.
NET_ASYNC_COMPLETE_WITH_MORE_RESULTS if mysql_next_result_nonblocking() API did
return with no more results.
New data structures to support asynchronous operations:
=======================================================
To keep track of the context associated with asynchronous operations
mysql_async struct will be introduced. Below is list of members added to
this structure.
struct mysql_async {
/* Buffer storing the rows result for cli_read_rows_nonblocking */
MYSQL_DATA *rows_result_buffer;
/* a pointer to keep track of the previous row of the current result row */
MYSQL_ROWS **prev_row_ptr;
/* Context needed to track the state of a connection being established */
struct mysql_async_connect *connect_context;
/* Status of the current async op */
enum mysql_async_operation_status async_op_status;
/* Size of the current running async query */
size_t async_query_length;
/* If a query is running, this is its state */
enum mysql_async_query_state_enum async_query_state;
/* context needed to support metadata read operation */
unsigned long *async_read_metadata_field_len;
MYSQL_FIELD *async_read_metadata_fields;
MYSQL_ROWS async_read_metadata_data;
unsigned int async_read_metadata_cur_field;
/* a pointer to keep track of the result sets */
struct MYSQL_RES *async_store_result_result;
};
struct mysql_async will be added to struct MYSQL_EXTENSION. This way it will be
available in MYSQL struct through MYSQL::extension member.
struct MYSQL_EXTENSION {
struct st_mysql_trace_info *trace_data;
STATE_INFO state_change;
/* Struct to track the state of asynchronous operations */
struct mysql_async mysql_async_context;
};
struct mysql_async_connect keeps all context needed to establish asynchronous
connection.
struct mysql_async_connect {
/* state for the overall connection process */
MYSQL *mysql;
const char *host;
const char *user;
const char *passwd;
const char *db;
uint port;
const char *unix_socket;
ulong client_flag;
bool non_blocking;
ulong pkt_length;
char *host_info;
char buff[NAME_LEN + USERNAME_LENGTH + 100];
int scramble_data_len;
char *scramble_data;
const char *scramble_plugin;
char *scramble_buffer;
bool scramble_buffer_allocated;
struct mysql_async_auth *auth_context;
/* state for running init_commands */
bool saved_reconnect;
char **current_init_command;
/* context needed to establish asynchronous ssl connection */
ssl_exchange_state ssl_state;
#if defined(HAVE_OPENSSL)
SSL *ssl;
#endif
/* state function that will be called next */
csm_function state_function;
};
struct mysql_async_auth keeps all context needed to establish asynchronous
authentication.
struct mysql_async_auth {
MYSQL *mysql;
bool non_blocking;
char *data;
uint data_len;
const char *data_plugin;
const char *db;
const char *auth_plugin_name;
auth_plugin_t *auth_plugin;
MCPVIO_EXT mpvio;
ulong pkt_length;
int res;
char *change_user_buff;
int change_user_buff_len;
void* client_auth_plugin_state;
authsm_function state_function;
};
/* A state machine for connection itself. */
typedef mysql_state_machine_status (*csm_function)(mysql_async_connect *);
/* A state machine for authentication itself. */
typedef mysql_state_machine_status (*authsm_function)(mysql_async_auth *);
Asynchronous operations are broadly classified into 2 categories. 1. Connection
2. Query execution. This classification is defined in below enum
mysql_async_operation_status
enum mysql_async_operation_status {
ASYNC_OP_UNSET = 18000,
ASYNC_OP_CONNECT = 18010,
ASYNC_OP_QUERY = 18020
};
Query execution in an asynchronous fashion is broadly divided into 3 states
QUERY_IDLE, QUERY_SENDING, QUERY_READING_RESULT this state is defined in
mysql_async_query_state_enum
enum mysql_async_query_state_enum {
QUERY_IDLE = 0,
QUERY_SENDING,
QUERY_READING_RESULT
};
A new enum is introduced to represent state of an asynchronous operation. This
enum is exposed to end users to be able to declare in client applications to
check the state of asynchronous operation.
enum net_async_status {
NET_ASYNC_COMPLETE = 20100,
NET_ASYNC_NOT_READY,
NET_ASYNC_ERROR,
NET_ASYNC_COMPLETE_WITH_MORE_RESULTS
};
To define different states of an asynchronous SSL connection phase below enum
is introduced.
enum ssl_exchange_state {
SSL_REQUEST = 8100,
SSL_CONNECT = 8101,
SSL_COMPLETE = 8102,
SSL_NONE = 8103
};
A sample application is as below:
#include <'stdio.h'>
#include <'string.h'>
#include <'iostream'>
#include <'mysql.h'>
#include <'mysqld_error.h'>
using namespace std;
static char * c_host = "localhost";
static char * c_user = "root";
static char * c_auth = "";
static int c_port = 17171;
static char * c_sock = "/tmp/mysqld47.sock";
static char * c_dbnm = "";
void perform_arithmatic() {
cout<<"\n dummy function";
for (int i =0; i < 1000; i++)
i*i;
}
static void wl11381() {
MYSQL *mysql_local;
MYSQL_RES *result;
MYSQL_ROW row;
net_async_status status;
const char *stmt_text;
if (!(mysql_local = mysql_init(NULL))) {
cout<<"\n mysql_init() failed";
exit(1);
}
while ((status == mysql_real_connect_nonblocking(mysql_local, c_host, c_user,
c_auth, c_dbnm, c_port, c_sock, 0)) == NET_ASYNC_NOT_READY);
if (status == NET_ASYNC_ERROR) {
cout<<"\n mysql_real_connect_nonblocking() failed";
exit(1);
}
stmt_text = "SELECT * FROM db.test_table order by id";
/* run query in asynchronous way */
status = mysql_real_query_nonblocking(mysql_local, stmt_text,
(ulong)strlen(stmt_text));
/* do some other task */
perform_arithmatic();
while (status == NET_ASYNC_NOT_READY) {
status = mysql_real_query_nonblocking(mysql_local, stmt_text,
(ulong)strlen(stmt_text));
}
if (status == NET_ASYNC_ERROR) {
cout<<"\n mysql_real_query_nonblocking() failed";
exit(1);
}
status = mysql_store_result_nonblocking(mysql_local, &result);
/* do some other task */
perform_arithmatic();
while (status == NET_ASYNC_NOT_READY) {
status = mysql_store_result_nonblocking(mysql_local, &result);
}
if (!result) {
cout<<"\n mysql_store_result_nonblocking() found 0 records";
exit(1);
}
row = mysql_fetch_row(result);
if (strcmp(row[0], "10") == 0)
cout<<"\n ROW :" << row[0];
else
cout<<" \n wrong result fetched";
while ((status == mysql_fetch_row_nonblocking(result, &row)) !=
NET_ASYNC_COMPLETE);
/* 2nd row fetched */
if (strcmp(row[0], "20") == 0)
cout<<"\n ROW :"<< row[0];
else
cout<<" \n wrong result fetched";
status = mysql_fetch_row_nonblocking(result, &row);
/* do some other task */
perform_arithmatic();
if (status == NET_ASYNC_COMPLETE) {
if (strcmp(row[0], "30") == 0)
cout<<"\n ROW :"<< row[0];
else
cout<<" \n wrong result fetched";
} else {
while ((status == mysql_fetch_row_nonblocking(result, &row)) !=
NET_ASYNC_COMPLETE);
/* 3rd row fetched */
if (strcmp(row[0], "30") == 0)
cout<<"\n ROW :"<< row[0];
else
cout<<" \n wrong result fetched";
}
while ((status = mysql_fetch_row_nonblocking(result, &row)) !=
NET_ASYNC_COMPLETE);
if (row == NULL) { cout <<" \n Nor more rows to process."; }
while((status == mysql_free_result_nonblocking(result)) !=
NET_ASYNC_COMPLETE);
mysql_close(mysql_local);
}
int main(int argc, char ** argv)
{
/*
create below sql objects:
"DROP DATABASE IF EXISTS db;"
"CREATE DATABASE db;"
"USE db;"
"CREATE TABLE test_table(id INT);"
"INSERT INTO test_table VALUES (10), (20), (30);";
*/
wl11381();
}
build above program as follows:
gcc -g async_app.cc -std=c++11 -I/export/home/tmp/bhsatish/11381/include -o
async_app -L/usr/lib64/ -lstdc++ -L/export/home/tmp/bhsatish/11381/lib/ -
lmysqlclient
Asynchronous connection phase:
==============================
1. Client connects to server.
2. Server sends initial handshake packet along with authentication method data.
3. Client replies with initial response packet along with client side
authentication method data.
4. Server and client further send more packets once common authentication
method is agreed upon.
5. On success server sends OK packet where connection is successful else ERR
packet in case of failure.
With this WL all the above communication will be made asynchronous.
To achieve this several non-blocking APIs needs to be introduced which are
listed below:
1. mysql_real_connect_nonblocking - initialize all context needed to start
asynchronous connection, read and parse the handshake packets sent by
server.
2. New non blocking client authentication plugin APIs will be introduced as
part of it.
mysql_declare_client_plugin(AUTHENTICATION) will be extended with new API
to achieve asynchronous authentication.
enum net_async_status (*authenticate_user_nonblocking)(MYSQL_PLUGIN_VIO *vio,
struct MYSQL *mysql,
int *result);
This plugin API will executes a state machine defined in variable
mysql->extension->mysql_async_context->connect_context->
auth_context->client_auth_plugin_state
Presence of this non blocking call in plugin will ensure that plugin
supports asynchronous authentication.
3. Above client authentication plugin API further calls methods to read and
write to network. These APIs needs to be non blocking too, thus
MYSQL_PLUGIN_VIO is extended to add 2 more non blocking APIs.
enum net_async_status (*read_packet_nonblocking)(
struct MYSQL_PLUGIN_VIO *vio,
unsigned char **buf,
int *result);
"buf" parameter holds the data read which is sent by server.
"result" parameter holds error state zero for success nonzero for error.
enum net_async_status (*write_packet_nonblocking)(
struct MYSQL_PLUGIN_VIO *vio,
const unsigned char *pkt,
int pkt_len, int *result);
"pkt" parameter holds the data to be written.
"pkt_len" length of "pkt" to be sent to server.
"result" parameter holds error state zero for success nonzero for error.
Return value for above APIs:
NET_ASYNC_COMPLETE if asynchronous operation is complete.
NET_ASYNC_NOT_READY if operation is incomplete. In this case call this API
again until NET_ASYNC_COMPLETE is returned.
Note: If client initiates asynchronous connection and the underlying client side
authentication plugin does not support asynchronous communication then
connection is aborted and an error is reported.
New non blocking client authentication plugin API is available only for these
plugins: mysql_native_password, sha256_password, caching_sha2_password.
Its is not supported for other plugins like clear_text_password, ldap client
side plugin etc.
Before sending initial response packet from client to server, client can decide
upon establishing SSL connection too if server supports it.
Currently cli_establish_ssl() function obtains SSL connection. This WL
implements cli_establish_ssl_nonblocking() function to achieve non blocking
SSL connection.
Currently following statements are not supported with asynchronous APIs:
Load data infile statement, Load xml statements.
Asynchronous support with compression protocol enabled is also not supported.
Note: Asynchronous communication is supported only for TCPIP/SSL/SOCKET based
connections only.
One of the ways to implement asynchronous support is to use a finite state
machine. In this WL we define different states to represent different
operations w.r.t network read or write or query related states or connection
related states or state to represent authentication phase.
Different states are defined below:
This enum represents state of an asynchronous operation. State can be either
complete or yet to start.
enum net_async_status
{ NET_ASYNC_COMPLETE = 20100, /* async operation is complete */
NET_ASYNC_NOT_READY, /* async operation needs to be restarted again */
NET_ASYNC_ERROR /* async operation ended with an error */
};
This enum is to represent different asynchronous operations like reading the
network, writing to network, idle state, or complete state.
enum net_async_operation {
NET_ASYNC_OP_IDLE = 0, /* default state */
NET_ASYNC_OP_READING = 20200, /* used by my_net_read calls */
NET_ASYNC_OP_WRITING, /* used by my_net_write calls */
NET_ASYNC_OP_COMPLETE /* network read or write is complete */
};
Data is transferred between client and server in the form of packets. Thus
reading this packet is segregated into different states. This includes read
packet header, read packet body, read complete, or idle state. Below enum
represents the same.
enum net_async_read_packet_state {
NET_ASYNC_PACKET_READ_IDLE = 0, /* default packet read state */
NET_ASYNC_PACKET_READ_HEADER = 2030 /* read packet header */
NET_ASYNC_PACKET_READ_BODY, /* read packet contents */
NET_ASYNC_PACKET_READ_COMPLETE /* state to define if packet is
completely read */
};
Reading query results is segregated into different states as described below.
enum net_read_query_result_status {
NET_ASYNC_READ_QUERY_RESULT_IDLE = 0, /* default read query result state */
NET_ASYNC_READ_QUERY_RESULT_FIELD_COUNT = 20240,
/* read Ok or read field count sent
as part of COM_QUERY */
NET_ASYNC_READ_QUERY_RESULT_FIELD_INFO /* read metadata if field count > 0 */
};
As part of client server communication several COM_* commands are sent from
server. State of this communication is described in below enum.
enum net_send_command_status {
NET_ASYNC_SEND_COMMAND_IDLE = 0, /* default send command state */
NET_ASYNC_SEND_COMMAND_WRITE_COMMAND = 20340, /* send COM_* command */
NET_ASYNC_SEND_COMMAND_READ_STATUS = 20340 /* read result of above COM_*
command */
};
At a more higher level async operation is broadly classified into 3 phases.
Connection phase, phase of sending data to server (which is writing phase) and
reading data from server (which is reading phase). Below enum describes the
same.
enum net_async_block_state {
NET_NONBLOCKING_CONNECT = 20130, /* state describing connection phase */
NET_NONBLOCKING_READ = 20140, /* reading state */
NET_NONBLOCKING_WRITE = 20150 /* writing state */
};
Changes to vio:
===============
vio_read() and vio_write() functions read or write on the network. If there is
no data on the socket, then these calls will wait until data is present or
timeout and retry. In this WL based on a flag (is_blocking == false) we return
immediately if there is no data on the socket.
Similarly changes are made in vio_socket_connect() to ensure connection phase
is also asynchronous.
Changes to my_net_read/write:
=============================
my_net_read/my_net_write are high level calls used to read or write onto the
network. These functions internally call vio_read/write. Non blocking version
for these functions are introduced as part of this WL.
my_net_read_nonblocking: This function will read packet header followed by
packet contents. These 2 operations are driven by change in state. Initial
state is set to NET_ASYNC_PACKET_READ_IDLE which is then changed to
NET_ASYNC_PACKET_READ_HEADER. Reading packet header is defined by
net_read_packet_header_nonblock(). If this function
net_read_packet_header_nonblock
returns immediately with a return status set to NET_ASYNC_NOT_READY, then the
caller will again call same function my_net_read_nonblocking() to again
check if packet header is read or not based on the state saved in NET structure.
This way based on state of an asynchronous operations non-blocking functions
are called to finish the operations. Once packet contents are read using
net_read_data_nonblocking() we then reset the state to
NET_ASYNC_PACKET_READ_IDLE for next packet to be read.
State change when reading from network is as below:
NET_ASYNC_OP_IDLE -> NET_ASYNC_OP_READING -> NET_ASYNC_OP_COMPLETE ->
NET_ASYNC_OP_IDLE
State change when reading packet sent by server to client is as below:
NET_ASYNC_PACKET_READ_IDLE -> NET_ASYNC_PACKET_READ_HEADER ->
NET_ASYNC_PACKET_READ_BODY -> NET_ASYNC_PACKET_READ_COMPLETE ->
NET_ASYNC_PACKET_READ_IDLE
my_net_write_nonblocking: Similar to my_net_write except that this function
returns immediately by either setting return status to NET_ASYNC_NOT_READY or
NET_ASYNC_COMPLETE.
State change when writing from network is as below:
NET_ASYNC_OP_IDLE -> NET_ASYNC_OP_WRITING -> NET_ASYNC_OP_COMPLETE ->
NET_ASYNC_OP_IDLE
Changes during query execution:
===============================
Executing a query involves sending a query followed by reading results. Reading
results might include following:
1. Reading OK packet in case there is no result set.
2. Reading metadata.
3. Reading results sets by calling mysql_fetch_row or calling
mysql_store_result.
Sending query to server is done by call to mysql_send_query. mysql_send_query
further calls cli_advanced_command to write the query on the network. Thus a
non-blocking version for these API is introduced as part of this WL.
mysql_send_query_nonblocking followed by call to
cli_advanced_command_nonblocking which does send query to server in an
asynchronous way.
Reading response as part of sending a query is achieved by call to
mysql_read_query_result API. This API further calls cli_safe_read or
cli_read_metadata in case there is a metadata sent by server. Thus we have
mysql_read_query_result_nonblocking to achieve the same in asynchronous way.
Similarly mysql_fetch_row_nonblocking or mysql_store_result_nonblocking will
be introduced to read result set in an asynchronous way.
Changes during authentication phase:
====================================
After reading initial handshake packet from server, client establishes ssl
connection and then checks with client side authentication plugin to know if it
supports asynchronous communication or not. If it supports then plugin will
return true along with state machine set in
mysql->extension->mysql_async_context->auth_context->client_auth_plugin_state.
This enum represents a state machine applicable to this client plugin. Client
further calls plugin authentication API authenticate_user_nonblocking() to
obtain connection.
Client authentication plugin does following (in case of caching_sha2 plugin)
1. Read scramble sent by server.
2. Generate random string from password and above scramble and send to server.
3. Read response. Response can be fast authentication success or perform full
authentication.
4. For non ssl connection request public key
4.1 Read public key sent by server.
4.2. Encrypted password using public key and send to server.
5. If ssl connection is established send plain text password.
Above sequence of steps are mapped to represent a state flow defined by below
enum:
enum caching_sha2_password_plugin_state {
READING_PASSWORD = 1, /* read scramble */
WRITING_RESPONSE, /* write scrambled password */
CHALLENGE_RESPONSE, /* check for fastpath or full authentication */
REQUEST_PUBLIC_KEY, /* request public key from server */
READ_PUBLIC_KEY, /* read public key sent by server */
SEND_ENCRYPTED_PASSWORD, /* send encrypted password */
SEND_PLAIN_PASSWORD /* if ssl set send plain text password */
};
caching_sha2_password_auth_client_nonblocking: Initially state is set to
READING_PASSWORD, thus making plugin API read the scramble sent by server, then
state is changed to WRITING_RESPONSE. Reading of this password should be
asynchrounous, thus client_mpvio_read_packet_nonblocking() is implemented to
achieve this. Writing scrambled password is done by
client_mpvio_write_packet_nonblocking(), this call can return immediately or
complete the operation. If operation is not complete server calls this plugin
API again until operation is complete and change the state to
CHALLENGE_RESPONSE. This way based on what state client authentication plugin is
in execute that state to complete authentication phase without blocking.
Below sample code path executes asynchronous authentication operation:
int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
const char *data_plugin, const char *db) {
DBUG_ENTER("run_plugin_auth");
...
...
if (ctx->non_blocking) {
if (!(ctx->auth_plugin->is_async_authentication_supported(mysql))) {
net_async_status status = ctx->auth_plugin->authenticate_user_nonblocking(
(struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql, &ctx->res);
if (status == NET_ASYNC_NOT_READY) {
DBUG_RETURN(STATE_MACHINE_WOULD_BLOCK);
}
} else {
DBUG_RETURN(STATE_MACHINE_FAILED);
}
} else {
ctx->res = ctx->auth_plugin->authenticate_user(
(struct MYSQL_PLUGIN_VIO *)&ctx->mpvio, mysql);
}
...
}
Below enum represents state flow for sha256_password client plugin:
enum sha256_password_plugin_state {
READING_PASSWORD = 1, /* read scramble */
REQUEST_PUBLIC_KEY, /* request public key from server */
READ_PUBLIC_KEY, /* read public key sent by server */
SEND_ENCRYPTED_PASSWORD, /* send encrypted password */
SEND_PLAIN_PASSWORD /* if ssl set send plain text password */
};
Below enum represents state flow for native_password client plugin:
enum native_password_plugin_state {
READING_PASSWORD = 1, /* read scramble */
WRITING_RESPONSE, /* write scrambled password */
};
MCPVIO_EXT and MYSQL_PLUGIN_VIO are extended to support nonblocking read and
write operations. Below APIs are added to this structure.
enum net_async_status (*read_packet_nonblocking)(struct MYSQL_PLUGIN_VIO *vio,
unsigned char **buf,
int *result);
enum net_async_status (*write_packet_nonblocking)(
struct MYSQL_PLUGIN_VIO *vio, const unsigned char *pkt, int pkt_len,
int *result);
Changes during SSL connection:
==============================
Different states of an asynchronous SSL connection phase is as below:
enum ssl_exchange_state {
SSL_REQUEST = 8100,
SSL_CONNECT = 8101,
SSL_COMPLETE = 8102,
SSL_NONE = 8103
};
New nonblocking cli_establish_ssl_nonblocking function is introduced which will
set initial state to SSL_NONE during which we check if server supports ssl or
not, if CA certificate is required when ssl_mode is set to VERIFY_CA. Once
preliminary checks are done state is changed to SSL_REQUEST. In this state ssl
request packet is sent by client. If this network IO is complete, state is
changed to SSL_CONNECT. During SSL_CONNECT sslconnect() is called which can
return immediately or complete SSL handshake. If it returns immediately we need
to store all SSL context in struct mysql_async_auth , so that next call to same
function will ensure that SSL_new() is not called twice.
During SSL_REQUEST state my_net_write_nonblocking() is called
to write the packet containing ssl request to server in asynchronous way.
Copyright (c) 2000, 2025, Oracle Corporation and/or its affiliates. All rights reserved.