How does mysqli_poll work?

The mysqli_poll documentation is a bit sparse.

In this example, they create 3 identical arrays containing all MySQLi connections for validation, but if you read the parameter descriptions, this does not look right.

It seems to me that $read is an array in which connections are checked, but $error and $reject should be unsettled vars, which will be filled by the function if there are errors. It is right?

What happens when a function returns> = 1? How do you know which connections are ready to receive data? Is $read also modified? that is, reduced to established connections that really have data?

Finally, do sec and usec actually do something? If yes, then? I tried setting sec to 0 and usec to 1 (I assume this means 0 seconds + 1 microseconds = 1 microsecond total timeout), but it pauses for more than a second when I run a large request, so it doesn't seem to interrupt or causes an error when time runs out. What is he doing?

+6
source share
1 answer

TL DR: Your assumptions are correct, but be careful when coding.


mysqli_poll is a thin, convenient wrapper around the select socket , and knowing how socket_select works will give you a great way to understand this function.

mysqli_poll is only available if the base driver is mysqlnd , because only MySQL ND provides native socket-level access to the MySQL server. It is important to keep in mind that โ€œsocket-levelโ€ access makes polling possible, and why understanding the choice of socket is crucial for understanding the mysqli_poll function and limitations.

To answer your questions:

It seems to me that $ read is an array containing the connections to check, but $ error and $ reject should be unpopulated vars, which will be populated by the function if there are errors. It is right?

Yes, but not the whole picture. Notice the mysqli_poll signature:

int mysqli_poll (array & $ read, array & $ error, array & $ reject, int $ sec [, int $ usec])

All three arrays are passed by reference, which means that the PHP engine has the ability to change all three that will be. In the obvious case, it changes $error and $reject when any of the requested connections from $read is in an error or connection state.

But PHP will also modify $read when there is data waiting to be read. This is the key to answering your question:

What happens when a function returns> = 1? How do you know which connections are ready to receive data? Is the $ read file modified? that is, reduced to established connections that really have data?

Yes, and this is important and not obvious in the documents. $read will be changed to a list of connections that are ready to read. You will focus on them and make your business. But the imperative moment is obscured: if $read changed, what happens if you put the poll in a loop and try to read it again? Well, you will only read from a subset of what you do not want.

What most examples show when making selections in PHP is that the original $read array is copied to the new array before selecting select. On the man page for mysqli_poll notice this loop, which "flushes" the read array immediately before calling mysqli_poll:

 foreach ($all_links as $link) { $links[] = $errors[] = $reject[] = $link; } 

This is perhaps the most important point: each of these arrays passed to mysqli_poll will be changed when mysqli_poll ends: the arrays will be truncated so that only the affected connections will be mysqli_poll , so you must reset the arrays every time before you call mysqli_poll .

Another example is shown in this PHP note on socket_select . Notice how $read = $clients; before a choice?

To your last question:

Finally, do sec and usec really do something? If yes, then? I tried setting sec to 0 and usec to 1 (I assume this means 0 seconds + 1 microseconds = 1 microseconds of total latency), but it pauses for more than a second when I run a large request, so it seems to be does not break or cause an error when it expires. What is he doing?

Yes it works. These should represent PHP with an upper bound, expecting the data to become available for any of the connections in $read (but read on). This did not work for you because the minimum time is 1 second. When you set 0 per second, even if you have a microsecond > 0 , PHP interprets this as "wait forever."

As an additional note, tags for mysqli_poll can be highlighted.


Update: I was not near the computer to check last night. Now that I'm in, I have some comments to share.

test 1: long queries

 $ cat mysqli_poll_test $link = mysqli_connect(...); $sql = 'SELECT SLEEP(2), 1'; mysqli_query($link, $sql, MYSQLI_ASYNC); $links = array ($link); $begin = microtime(true); $i = 0; do { printf("start i=%d @ T+%.f\n", $i, (microtime(true)-$begin)); $read = $error = $reject = $links; mysqli_poll($read, $error, $reject, 1, 500000); printf( "finish i=%d, count(read, error, reject)=(%d, %d, %d) @ T+%f\n\n", $i++, count($read), count($error), count($reject), (microtime(true)-$begin) ); } while (count($links) !== count($read) + count($error) + count($reject)); $ php mysqli_poll_test start i=0 @ T+0.000012 finish i=0, count(read, error, reject)=(0, 0, 0) @ T+1.501807 start i=1 @ T+1.501955 finish i=1, count(read, error, reject)=(1, 0, 0) @ T+2.001353 

In this test, a long query is a simple sleep for 2 seconds on a MySQL server. The mysqli_poll is 1.5 seconds. As expected, after 1.5 seconds the survey returns. Also, as expected, there is no data ready to read, so restarting do .. while . After the remaining half second, a poll returns indicating that one link is ready for reading. This is expected because the query takes only 2 seconds, and polling shows that it is very close to exactly two seconds.

If you change the polling timeout by half a second and re-run:

 // changed this from 1 to 0 --------V mysqli_poll($read, $error, $reject, 0, 500000); 

The survey begins in half a second, and the cycle runs four times, as expected. If you change it to 1 microsecond, as in your example, it will exit after 1 microsecond. And if you change it to 0 seconds and 0 microseconds, it will work as quickly as possible.

So, I was definitely wrong when I said that 0 means wait forever.

test 2: several requests, some error and some lengthy work with timeouts

Change our script for a few more links and try again:

 $link0 = mysqli_connect(...); $link1 = mysqli_connect(...); $link2 = mysqli_connect(...); $sql0 = 'SELECT SLEEP(2) AS wait, 1 AS num'; $sql1 = 'SELECT foo FROM'; $sql2 = 'SELECT 2 AS num'; mysqli_query($link0, $sql0, MYSQLI_ASYNC); mysqli_query($link1, $sql1, MYSQLI_ASYNC); mysqli_query($link2, $sql2, MYSQLI_ASYNC); $links = array ($link0, $link1, $link2); $begin = microtime(true); $i = 0; do { printf("start i=%d @ T+%.f\n", $i, (microtime(true)-$begin)); $read = $error = $reject = $links; $count = mysqli_poll($read, $error, $reject, 1, 500000); if (0 < $count) { foreach ($links as $j => $link) { $result = mysqli_reap_async_query($link); if (is_object($result)) { printf("link #%d, row=%s\n", $j, json_encode($result->fetch_assoc())); mysqli_free_result($result); } else if (false !== $result) { printf("link #%d, output=%s\n", $j, $link); } else { printf("link #%d, error=%s\n", $j, mysqli_error($link)); } } } printf( "finish i=%d, count(read, error, reject)=(%d, %d, %d) @ T+%f\n\n", $i++, count($read), count($error), count($reject), (microtime(true)-$begin) ); } while (count($links) !== count($read) + count($error) + count($reject)); 

In this test, I expect two results to be resolved immediately: one syntax error, and the other a data row. I also expect this to take 1.5 seconds since a request sleeping 2 seconds will not be allowed until the timeout expires. This is not true:

 start i=0 @ T+0.000002 link #0, row={"wait":"0","num":"1"} link #1, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 link #2, row={"num":"2"} finish i=0, count(read, error, reject)=(1, 0, 0) @ T+2.001756 start i=1 @ T+2.001827 finish i=1, count(read, error, reject)=(0, 0, 3) @ T+3.503024 

It waits until the SLEEP(2) request is resolved, violating the assertion that the timeout was the upper limit of the wait. The reason this happens is mysqli_reap_async_query : we repeat all the links, and each of them is asked to be reapers. The harvest process waits until the request completes.

test 3: long queries with target reception:

Same as test # 2, but this time let me be smart about what we reap.

 $ cat mysqli_poll.php <?php $link0 = mysqli_connect(...); $link1 = mysqli_connect(...); $link2 = mysqli_connect(...); $sql0 = 'SELECT SLEEP(2) AS wait, 1 AS num'; $sql1 = 'SELECT foo FROM'; $sql2 = 'SELECT 2 AS num'; mysqli_query($link0, $sql0, MYSQLI_ASYNC); mysqli_query($link1, $sql1, MYSQLI_ASYNC); mysqli_query($link2, $sql2, MYSQLI_ASYNC); $links = array ($link0, $link1, $link2); $begin = microtime(true); $i = 0; do { printf("start i=%d @ T+%.f\n", $i, (microtime(true)-$begin)); $read = $error = $reject = $links; $count = mysqli_poll($read, $error, $reject, 1, 500000); printf( "check i=%d, count(read, error, reject)=(%d, %d, %d) @ T+%f\n", $i, count($read), count($error), count($reject), (microtime(true)-$begin) ); if (0 < $count) { reap('read', $read); reap('error', $error); reap('reject', $reject); } else { printf("timeout, no results\n"); } printf("finish i=%d\n\n", $i++); } while (count($links) !== count($read) + count($error) + count($reject)); function reap($label, array $links) { foreach ($links as $link) { $result = mysqli_reap_async_query($link); if (is_object($result)) { printf("%s, row=%s\n", $label, json_encode($result->fetch_assoc())); mysqli_free_result($result); } else if (false !== $result) { printf("%s, output=%s\n", $label, $link); } else { printf("%s, error=%s\n", $label, mysqli_error($link)); } } } 

And now run it.

 $ php mysqli_poll.php start i=0 @ T+0.000003 check i=0, count(read, error, reject)=(1, 0, 0) @ T+0.001007 read, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 finish i=0 start i=1 @ T+0.001256 check i=1, count(read, error, reject)=(1, 0, 1) @ T+0.001327 read, row={"num":"2"} reject, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 finish i=1 start i=2 @ T+0.001627 check i=2, count(read, error, reject)=(0, 0, 2) @ T+1.503261 timeout, no results finish i=2 start i=3 @ T+1.503564 check i=3, count(read, error, reject)=(1, 0, 2) @ T+2.001390 read, row={"wait":"0","num":"1"} reject, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 reject, error= finish i=3 

Much better. Each request is resolved at a convenient time, with filling in the corresponding arrays. An important difference in this example compared to the previous one is that we iterate over each of the modified arrays. This runs counter to some documentation that shows iteration over all links.

I opened the documentation for bug # 70505 .

+10
source

All Articles