WL#13510: COMPRESSION PROTOCOL FOR ASYNC CLIENT
Affects: Server-8.0
—
Status: Complete
We implemented asynchronous clients support through WL#11381 and added compression protocol support for usual clients through WL#12475. This worklog will enable compression protocol support for asynchronous clients. It will reduce the network traffic across data-centers. This worklog is based on Facebook's contribution.
FR-1: Asynchronous client must be able to send/receive compressed packets. FR-2: Asynchronous client must use compression algorithms supported by the server. For instance : 'zstd' and 'zlib'. FR-3: Asynchronous client must be able to execute single or multiple statements. FR-4: Wire format of the compressed packet must be same as it is sent by the usual blocking client.
WL#11381: Add asynchronous support into the mysql protocol
WL#12475: Protocol Changes to specify compression configuration for connections
WL#12475: Protocol Changes to specify compression configuration for connections
Background: ---------- Wire format of the compressed packet is not changed. Compressed packet consists 7 byte packet header followed by the payload. For instance : The first meta packet consists information about the subsequent packet. Both of them have packet header of 7 bytes followed by the payload. | len |cpn|uncompress len| payload size | packet num | command (COM_*)| e.g. | 5 | 0 | 000 | 560 | 0 | 3 | ∆ | | 3 byte | 1 byte | 1 byte | └──────────────────────┴──────────────┴────────────┴────────────────┘ | len |cpn|uncompress len| compressed payload | e.g. | 150 | 1| 560 | compress(payload) | ∆ V | | | | | | └──────┼──────────────┼─────────┘ | | | | 4 byte | 3 byte | └──────────┴──────────────┘ Refer the manual for the more details : https://dev.mysql.com/doc/dev/mysql- server/latest/page_protocol_basic_compression_packet.html (H1) Send the compressed packet through asynchronous client ------------------------------------------------------ (1) The first async state machine is to prepare the packet for writing on VIO. This state is named NET_ASYNC_OP_IDLE. Async client will prepare the compressed packet during this state. (2) Following two variables will be added to the NET_ASYNC structure to keep track of compressed data packets. NET_ASYNC { + unsigned char **compressed_write_buffers; + size_t compressed_buffers_size; } (3) The compressed packets are represented through io vector structure. This IO Vector is written to the VIO in the next async state known as NET_ASYNC_OP_WRITING. (4) Control may go back to the client during NET_ASYNC_OP_WRITING state while packet is being written to the VIO. In the NET_ASYNC_OP_IDLE state, while async state machine is compressing the data control does not return to the client. (H2) Read the compressed packet through asynchronous client ------------------------------------------------------ (1) Like usual nonblocking read, async client will read the header of compressed packet in the NET_ASYNC_PACKET_READ_HEADER state. It will retrieve the length and the packet sequence number. (2) Async state machine will fall through in NET_ASYNC_PACKET_READ_BODY state. While reading the packet from the VIO, it may return the control to the client. If the async state machine is able to read the compressed data then it uncompresses the data before moving itself to the NET_ASYNC_COMPLETE state. (3) The uncompressed data may comprises multiple packets. Hence the data is parsed until all packets are read in the uncompressed payload. (4) In cases where the payload length is more than MAX_PACKET_LENGTH, the packet is divided into the multiple packets. The subsequent packets comprises data in continuation. In multibyte packets we could directly read the data in the subsequent packets. There shall be handling of such packets. (H3) Enable packets compression for asynchronous clients --------------------------------------------------- User needs to set the compression algorithm name and if supported compression level in the asynchronous client to able the packets compression. For instance : ------------- // Set the compression algorithm name if (mysql_options(mysql_local, MYSQL_OPT_COMPRESSION_ALGORITHMS, "zstd")) exit(1); // Set the compression level. This is optional. if (mysql_options(mysql_local, MYSQL_OPT_ZSTD_COMPRESSION_LEVEL, 4)) exit(1); if(mysql_real_connect(mysql_local, opt_host, opt_user, opt_password, current_db, opt_port, opt_unix_socket, client_flag)) exit(1); stmt_text = "SELECT col2 FROM test_table"; // 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)); }
(L1) Sending the compressed packet asynchronously -------------------------------------------- (1) net_write_command_nonblocking() API writes to the wire in various steps. These steps are called net_async_operation states. In the first state, NET_ASYNC_OP_IDLE, packet is prepared to be written. In the second state packet is sent to the wire. The packet to be written is represented by a structure known as io_vec. net_write_command_nonblocking () { switch (net_async->async_operation) { case NET_ASYNC_OP_IDLE: if (!begin_packet_write_state()) { return NET_ASYNC_COMPLETE; } net_async->async_operation = NET_ASYNC_OP_WRITING; /* fallthrough */ case NET_ASYNC_OP_WRITING: status = net_write_vector_nonblocking(); if (status == NET_ASYNC_COMPLETE) { return NET_ASYNC_COMPLETE; } else return NET_ASYNC_NOT_READY; } (2) The compress packet is prepared during the first state NET_ASYNC_OP_IDLE. The payload is compressed and added to the io_vec. The compressed and uncompressed payload length are added to the packet header. begin_packet_write_state { struct io_vec *vec =; for (size_t packet_num = 0; packet_num < packet_count; ++packet_num) { /* For each packet the iovec points to the packet header and payload buffer as following. */ vec[packet_num][0] = | len |cpn|uncompress len| payload size | | packet num | command (COM_*)| vec[packet_num[1] = | prefix | vec[packet_num][2] = | len |cpn|uncompress len| | compressed payload | } } (L2) Reading the compressed packet asynchronously : -------------------------------------------- (1) my_net_read_nonblocking() API reads the packets asynchronously. This API will call the respective read method depending upon the compress or uncompress packet to be read. if (net->compress) { if (my_net_read_compressed_nonblocking(net, len_ptr, complen_ptr) == NET_ASYNC_NOT_READY) { return NET_ASYNC_NOT_READY; } } else if (net_read_packet_nonblocking(net, len_ptr, complen_ptr) == NET_ASYNC_NOT_READY) { return NET_ASYNC_NOT_READY; } (2) Since the wire format is not changed, the pattern to read the uncompressed packet is same for synchronous and asynchronous clients both. We could read the compressed packet as following. read_compressed_packets { // initialize offsets in the NET structure initialize_offsets(net); for (;;) { if(process_buffer(net)) break; if(asynchronous_client) read_packet_nonblocking(packet); // packet = net->buff uncompress_packet(packet) else read_packet_uncompress_it(packet); } // Update the offsets in the NET structure after the packet is read. update_offsets(net); } (3) The nonblocking packet read happens in different steps. These steps are called net_read_packet_nonblocking states. Control may reach to the client while the packet is being read. During uncompress operation control does not reach to the client. read_packet_nonblocking() { switch (net_read_packet_nonblocking) { case NET_ASYNC_PACKET_READ_HEADER: if (net_read_packet_header_nonblocking(net, &err) == NET_ASYNC_NOT_READY) { return NET_ASYNC_NOT_READY; } case NET_ASYNC_PACKET_READ_BODY: if (net_read_data_nonblocking(net, net_async->async_packet_length, &err) == NET_ASYNC_NOT_READY) { return NET_ASYNC_NOT_READY; } } if(is_compressed) { my_uncompress(compress_context, net->buff, uncomp_length, original_len); } }
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.