RetryStrategy::errorCode()
last analyzed

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
c 0
b 0
f 0
nc 1
1
<?php
2
3
namespace Ez\DbLinker\RetryStrategy;
4
5
use Exception;
6
use Ez\DbLinker\Slave;
7
use stdClass;
8
use Ez\DbLinker\Driver\Connection\MasterSlavesConnection;
9
use Ez\DbLinker\Driver\Connection\RetryConnection;
10
11
trait RetryStrategy
12
{
13
    private $retryLimit;
14
15
    public function __construct($retryLimit = 8)
16
    {
17
        $this->retryLimit = $retryLimit;
18
    }
19
20
    /**
21
     * @param Exception $exception
22
     * @param RetryConnection $connection
23
     * @return bool
24
     */
25
    public function shouldRetry(Exception $exception, RetryConnection $connection): bool
26
    {
27
        if (!$this->canRetry($connection)) {
28
            return false;
29
        }
30
        $strategy = $this->errorCodeStrategy($this->errorCode($exception));
31
        return $this->applyStrategy($strategy, $connection);
32
    }
33
34
    public function retryLimit()
35
    {
36
        return $this->retryLimit > 0 ? (int) $this->retryLimit : 0;
37
    }
38
39
    private function canRetry(RetryConnection $connection)
40
    {
41
        return $this->retryLimit > 0 && $connection->transactionLevel() === 0;
42
    }
43
44
    private function errorCodeStrategy($errorCode)
45
    {
46
        $strategy = (object) [
47
            'retry' => true,
48
            'wait' => 0,
49
            'changeServer' => false,
50
            'reconnect' => false,
51
        ];
52
        $errorCodeStrategies = $this->errorCodeStrategies();
53
        if (array_key_exists($errorCode, $errorCodeStrategies)) {
54
            foreach ($errorCodeStrategies[$errorCode] as $behavior => $value) {
55
                $strategy->$behavior = $value;
56
            }
57
            return $strategy;
58
        }
59
        return (object) ['retry' => false];
60
    }
61
62
    private function applyStrategy(stdClass $strategy, RetryConnection $connection) {
63
        if ($strategy->retry === false || !$this->changeServer($strategy, $connection)) {
64
            return false;
65
        }
66
        sleep($strategy->wait);
67
        $this->reconnect($strategy, $connection);
68
        $this->retryLimit--;
69
        return true;
70
    }
71
72
    private function changeServer(stdClass $strategy, RetryConnection $connection): bool
73
    {
74
        if (!$strategy->changeServer) {
75
            return true;
76
        }
77
        /** @var MasterSlavesConnection $wrappedConnection */
78
        $wrappedConnection = $connection->wrappedConnection();
79
        if ($wrappedConnection instanceof MasterSlavesConnection && $wrappedConnection->getLastConnection() instanceOf Slave) {
80
            $wrappedConnection->getLastConnection()->disable();
81
            return true;
82
        }
83
        return false;
84
    }
85
86
    private function reconnect(stdClass $strategy, RetryConnection $connection): void
87
    {
88
        if ($strategy->reconnect) {
89
            $connection->close();
90
        }
91
    }
92
93
    protected abstract function errorCodeStrategies();
94
    protected abstract function errorCode(Exception $exception);
95
}
96