2

In Illuminate/Database/Connectors/Connector.php

    protected function createPdoConnection($dsn, $username, $password, $options)
    {
        if (class_exists(PDOConnection::class) && ! $this->isPersistentConnection($options)) {
            return new PDOConnection($dsn, $username, $password, $options);
        }

        return new PDO($dsn, $username, $password, $options);
    }

The above function will execute forever at new PDO($dsn, $username, $password, $options) at a high packet loss rate network environment.

What I want is to not continue waiting for that after 30 seconds and return error/throw an exception (for clarification of why I want this: after that my application will try to connect to other slave databases instead).

The base line is that I don't think I should modify any code in Vendor.

The logic of generating the $options variable is set inside config/database.php and I did the following:

'options' => extension_loaded('pdo_mysql') ? array_filter([
                ...
                PDO::ATTR_TIMEOUT => 1,
            ]) : [],

The result is that an exception will be thrown but will be caught in the same Connector.php in the following function:

    public function createConnection($dsn, array $config, array $options)
    {
        [$username, $password] = [
            $config['username'] ?? null, $config['password'] ?? null,
        ];

        try {
            return $this->createPdoConnection(
                $dsn, $username, $password, $options
            );
        } catch (Exception $e) {
            return $this->tryAgainIfCausedByLostConnection(
                $e, $dsn, $username, $password, $options
            );
        }
    }

And the new PDO() line will be executed again. Basically there are some similar try catch things after that but essentially it never actually throws the exception into the application code but keeps reconnecting.

EDIT:

Actually I found out the Laravel code actually throws the exception after 4 retries but in most of times one of the retry will not throw the exception properly despite PDO::ATTR_TIMEOUT being set. PDO::ATTR_ERRMODE is also tried but it does not change.

I am suspecting that since the packet loss rate is 95% and not 100%, the connection is in the process of being established but at a very slow rate.

So I guess my question now is, is there a way to force kill an establishing connection after a timeout?

cr001
  • 655
  • 4
  • 16
  • Reading [this line](https://github.com/laravel/framework/blob/277c2fbd0cebd2cb194807654d870f4040e288c0/src/Illuminate/Database/Connectors/ConnectionFactory.php#L240) it makes me suspect that if you make your own connector (in your own code, not in vendor) and bind via `app()->instance('db.connector.mysql', new MyCustomMySqlConnector(...))` Laravel should use your custom connector rather than the default one. Have not tested it though – apokryfos Sep 18 '21 at 06:34
  • @apokryfos After some more investigation I found out it is actually not the Laravel Connector that's causing the issue, it will actually throw the exception after 4 retries (it's not an infinite loop but rather same code called four times situation), if ALL the 4 retries correctly throws the exception. However the issue actually happens because at cone of the retry the `new PDO()` DOES NOT throw an exception even though the option `PDO::ATTR_TIMEOUT` is given. I also tried forcing the `PDO::ATTR_ERRMODE` without success. – cr001 Sep 18 '21 at 06:55
  • @apokryfos I have updated my newest findings. I think if there is a way to force kill an establishing but very slow connection my problem would be solved. However I am not even sure if this is even possible. On the other hand I am pretty sure php and mysql must have a way for high-packet-loss networks as this seems a very general problem. – cr001 Sep 18 '21 at 07:01

0 Answers0