diff options
author | Dmitry Shulga <Dmitry.Shulga@Sun.COM> | 2010-09-16 17:24:27 +0700 |
---|---|---|
committer | Dmitry Shulga <Dmitry.Shulga@Sun.COM> | 2010-09-16 17:24:27 +0700 |
commit | be794bc5eb1c355c86a94c8988faed0d4c40cf69 (patch) | |
tree | 320d71f7dfbb88a1fa690aaeff7105a4a1e9d754 /sql/net_serv.cc | |
parent | f601c035c51cfd467faf681925ee5d0178384100 (diff) | |
download | mariadb-git-be794bc5eb1c355c86a94c8988faed0d4c40cf69.tar.gz |
Fixed bug#42503 - "Lost connection" errors when using
compression protocol.
The loss of connection was caused by a malformed packet
sent by the server in case when query cache was in use.
When storing data in the query cache, the query cache
memory allocation algorithm had a tendency to reduce
the amount of memory block necessary to store a result
set, up to finally storing the entire result set in a single
block. With a significant result set, this memory block
could turn out to be quite large - 30, 40 MB and on.
When such a result set was sent to the client, the entire
memory block was compressed and written to network as a
single network packet. However, the length of the
network packet is limited by 0xFFFFFF (16MB), since
the packet format only allows 3 bytes for packet length.
As a result, a malformed, overly large packet
with truncated length would be sent to the client
and break the client/server protocol.
The solution is, when sending result sets from the query
cache, to ensure that the data is chopped into
network packets of size <= 16MB, so that there
is no corruption of packet length. This solution,
however, has a shortcoming: since the result set
is still stored in the query cache as a single block,
at the time of sending, we've lost boundaries of individual
logical packets (one logical packet = one row of the result
set) and thus can end up sending a truncated logical
packet in a compressed network packet.
As a result, on the client we may require more memory than
max_allowed_packet to keep, both, the truncated
last logical packet, and the compressed next packet.
This never (or in practice never) happens without compression,
since without compression it's very unlikely that
a) a truncated logical packet would remain on the client
when it's time to read the next packet
b) a subsequent logical packet that is being read would be
so large that size-of-new-packet + size-of-old-packet-tail >
max_allowed_packet.
To remedy this issue, we send data in 1MB sized packets,
that's below the current client default of 16MB for
max_allowed_packet, but large enough to ensure there is no
unnecessary overhead from too many syscalls per result set.
Diffstat (limited to 'sql/net_serv.cc')
-rw-r--r-- | sql/net_serv.cc | 12 |
1 files changed, 11 insertions, 1 deletions
diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 38ab3a70136..f3f6f1bd9a0 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -170,7 +170,17 @@ my_bool net_realloc(NET *net, size_t length) DBUG_ENTER("net_realloc"); DBUG_PRINT("enter",("length: %lu", (ulong) length)); - if (length >= net->max_packet_size) + /* + When compression is off, net->where_b is always 0. + With compression turned on, net->where_b may indicate + that we still have a piece of the previous logical + packet in the buffer, unprocessed. Take it into account + when checking that max_allowed_packet is not exceeded. + This ensures that the client treats max_allowed_packet + limit identically, regardless of compression being on + or off. + */ + if (length >= (net->max_packet_size + net->where_b)) { DBUG_PRINT("error", ("Packet too large. Max size: %lu", net->max_packet_size)); |