Bug #97853 main.ssl-big-packet fails for async client
Submitted: 2 Dec 2019 18:18 Modified: 18 Mar 2020 13:44
Reporter: Manuel Ung Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S3 (Non-critical)
Version:8.0.17 OS:Any
Assigned to: CPU Architecture:Any

[2 Dec 2019 18:18] Manuel Ung
Description:
The main.ssl-big-packet mtr test fails for the async client because the client/server hangs, ending in timeout.

The problem is that the mysqltest.cc driver calls ppoll when NET_ASYNC_NOT_READY is returned. However, the async client sometimes returns NET_ASYNC_NOT_READY even when it isn't necessarily waiting for more IO.

You can verify this by removing the ppoll and spinning in a loop:

diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index 30d842436d1..ceaa815aa5f 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -901,10 +901,6 @@ static MYSQL_RES *async_mysql_store_result_wrapper(MYSQL *mysql) {
   while (mysql_store_result_nonblocking(mysql, &result) ==
          NET_ASYNC_NOT_READY) {
     t.check();
-    NET_ASYNC *net_async = NET_ASYNC_DATA(&(mysql->net));
-    int result = socket_event_listen(net_async->async_blocking_state,
-                                     mysql_get_socket_descriptor(mysql));
-    if (result == -1) return NULL;
   }
   return result;
 }

How to repeat:
./mtr --async-client main.ssl-big-packet

Suggested fix:
Ideally, we should not be returning NET_ASYNC_NOT_READY up the API if there is no IO to do.

Most async clients on upon receiving NET_ASYNC_NOT_READY, will not retry right away in a spinning fashion, and will most likely call ppoll on the file descriptor. This can block indefinitely if there is no actual io to do.
[2 Dec 2019 18:55] Manuel Ung
Another possible fix is just to call ppoll with timeout.
[2 Dec 2019 21:15] MySQL Verification Team
Thank you for the bug report.
[3 Dec 2019 14:11] Manuel Ung
Actually, the problem here seems to be mysqltest.cc should be listening for both reads/writes with SSL, because writes can happen during reads (and vice versa).

This change also fixes the issue:

diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index bca4a6f3573..639f456fb28 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -857,19 +857,11 @@ static int socket_event_listen(net_async_block_state async_blocking_state,
   int result;
   pollfd pfd;
   pfd.fd = fd;
-  switch (async_blocking_state) {
-    case NET_NONBLOCKING_READ:
-      pfd.events = POLLIN;
-      break;
-    case NET_NONBLOCKING_WRITE:
-      pfd.events = POLLOUT;
-      break;
-    case NET_NONBLOCKING_CONNECT:
-      pfd.events = POLLIN | POLLOUT;
-      break;
-    default:
-      DBUG_ASSERT(false);
-  }
+  /*
+    Listen to both in/out because SSL can perform reads during writes (and
+    vice versa).
+  */
+  pfd.events = POLLIN | POLLOUT;
   result = poll(&pfd, 1, -1);
   if (result < 0) {
     perror("poll");
[18 Mar 2020 13:44] Paul DuBois
Posted by developer:
 
Fixed in 8.0.21.

Work was done for test suite. No changelog entry required.