friends-of-reactphp / mysql

Async MySQL database client for ReactPHP.
MIT License
334 stars 66 forks source link

Connection errors for lazy connections #121

Closed pavarnos closed 4 years ago

pavarnos commented 4 years ago

Hi. Is there a canonical way to handle connection errors for lazy connections (eg an invalid connection string)?

I tried

        $database = (new \React\MySQL\Factory($loop))->createLazyConnection('die-horribly');
        $database->on(
            'error',
            function ($error) {
                // this error never happens
                die($error);
            }
        );
        $database->query('select database()')->done(
            function (QueryResult $result) {
                // cannot get to here because of the bad mysql dsn
                var_dump($result->resultRows);
            },
            function (\Throwable $ex) {
                // this is absorbed by the promise?
                throw $ex;
            }
        );

What I want is a way to catch the failed connection from any query anywhere.... (deeply nested)

thanks!

pavarnos commented 4 years ago

Similarly for invalid queries: I'm not able to catch an exception or see a failed promise

clue commented 4 years ago

@pavarnos That's an excellent question!

Each query() call returns a promise that will be rejected with an Exception on error. You can react to this by using the second argument to the then() function like in your example. You can see, print and/or log the exception message to see what problem occurred.

If you throw from a promise rejection handler, this will result in a new promise that is also rejected. In your case, you do not (want to) attach a second handler to handle this rejection, so this will be discarded by default. We're working on https://github.com/reactphp/promise/issues/87 to improve this default error handling in the future. Here's how you could potentially see this:

$db->query('FOO')->then(null, function (Exception $e) {
    throw $e;
})->then(null, function (Exception $e) {
    echo $e->getMessage() . PHP_EOL;
});

In your case, you can probably just change your throw statement to an `echo´ statement and you should be able to see the error.

The error event is only useful for non-lazy connections (see also https://github.com/friends-of-reactphp/mysql#error-event). Lazy connections will automatically create a new connection when an internal error occurs, so this event never fires here.

I hope this helps :+1:

pavarnos commented 4 years ago

Thank you! Removing the throw helped for query(). I also see where my original question was unclear. My apologies. Here is a fuller example:

declare(strict_types=1);

require 'vendor/autoload.php';

$loop     = \React\EventLoop\Factory::create();
$database = (new \React\MySQL\Factory($loop))->createLazyConnection('blam it must blow up');
$database->on(
    'error',
    function ($error) {
        // this error never happens
        var_dump($error);
    }
);
$stream      = $database->queryStream('boom a terrible explosion');
$transformer = new \Clue\React\Flux\Transformer(
    1, function ($data) {
    var_dump('handler', $data);
    return \React\Promise\resolve(true);
}
);
$transformer->on(
    'data',
    function ($data) {
        var_dump('data', $data);
    }
);
$transformer->on(
    'end',
    function () {
        var_dump('end');
    }
);
$transformer->on(
    'error',
    function (\Throwable $ex) {
        var_dump($ex);
    }
);
$stream->pipe($transformer);

$database->quit();
$loop->run();

when I run this (invalid connection, invalid query) I get no output at all. It seems that queryStream() is soaking up all failures?

Ahhh... but I see my problem as I write this for you... I forgot to add

$stream->on(
    'error',
    function ($ex) {
        var_dump($ex);
    }
);

So now I see the expected output. My bad. I posted this here for those who follow and may have the same problem. I am still used to the old OOP way of things that die throwing exceptions....

clue commented 4 years ago

[…] Ahhh... but I see my problem as I write this for you... I forgot to add

$stream->on(
    'error',
    function ($ex) {
        var_dump($ex);
    }
);

So now I see the expected output. My bad. I posted this here for those who follow and may have the same problem. I am still used to the old OOP way of things that die throwing exceptions....

This is indeed what was missing from your first example, happy to hear you've got this worked out already! I agree it takes some time to get used to, but it's well worth it :-)

pavarnos commented 4 years ago

yes. thank you for building these amazing tools. and for your help