php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #70505 mysqli_poll documentation leads to apparent violation of timeout
Submitted: 2015-09-15 15:15 UTC Modified: 2017-01-28 17:25 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: bishop@php.net Assigned:
Status: Open Package: MySQLi related
PHP Version: 5.6.13 OS: any? or 3.14.27-25.47.amzn1.x86_
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2015-09-15 15:15 UTC] bishop@php.net
Description:
------------
When mysqli_poll is given a single link in its read array, and the async query on that link runs longer than the timeout, the timeout expires and poll returns (correctly) that nothing is ready to read.

However, in the same scenario when there are multiple links to read, mysqli_poll ignores the timeout and returns only when the longest running query resolves.

The man page is light on this function, so maybe I am misunderstanding how timeout should work.  Also, there's further discussion at this SO post: http://stackoverflow.com/a/32576278/2908724

Test script:
---------------
$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));


Expected result:
----------------
start i=0 @ T+0.000000
link #0, row=null
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, 1, 0) @ T+1.500000

start i=1 @ T+1.500000
link #0, row={"wait":"0",a"num":"2"}
link #1, row=null
link #2, row=null
finish i=0, count(read, error, reject)=(1, 0, 2) @ T+2.000000


Actual result:
--------------
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


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-09-15 16:27 UTC] bishop@php.net
-Summary: mysqli_poll does not honor timeout when multiple queries given +Summary: mysqli_poll documentation leads to apparent violation of timeout -Package: MySQLi related +Package: Documentation problem
 [2015-09-15 16:27 UTC] bishop@php.net
The documentation for mysqli_poll suggests iterating through the links after running the poll:

    foreach ($links as $link) {
        if ($result = $link->reap_async_query()) {
            print_r($result->fetch_row());
            if (is_object($result))
                mysqli_free_result($result);
        } else die(sprintf("MySQLi Error: %s", mysqli_error($link)));
        $processed++;
    }

This is not a good way to go about it when you have queries of different run-times, because mysqli_reap_async_query() blocks until the query resolves, which is contrary to the nature of the async poll.

An approach that respects multiple queries of different run times is to run a reaping function over each of the modified arrays:

    $count = mysqli_poll($read, $error, $reject, 1);
    if (0 < $count) {
        array_walk($read,   'reap');
        array_walk($error,  'reap');
        array_walk($reject, 'reap');
    }


function reap($link) {
    $result = mysqli_reap_async_query($link);
    if (is_object($result)) {
        print_r($result->fetch_assoc()));
        mysqli_free_result($result);
    } else if (false !== $result) {
        print_r($link);
    } else {
        die(mysqli_error($link));
    }
}

This also handles the case where the query is a CRUD and the result of reap_async_query isn't a result object.
 [2017-01-28 17:25 UTC] cmb@php.net
-Type: Bug +Type: Documentation Problem -Package: Documentation problem +Package: MySQLi related
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Wed Dec 12 08:01:25 2018 UTC