Skip to content

Commit d947378

Browse files
committed
Do not start idle timer when lazy connection is already closed
When an operation fails because the underlying connection is closed, we should never start an idle timer. There used to be a race condition that the connection close event was detected before cancelling the pending commands. We avoid this by checking the connection state before starting an idle timer when an operation fails.
1 parent c420ca0 commit d947378

File tree

2 files changed

+24
-1
lines changed

2 files changed

+24
-1
lines changed

src/Io/LazyConnection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ private function idle()
8989
{
9090
--$this->pending;
9191

92-
if ($this->pending < 1 && $this->idlePeriod >= 0) {
92+
if ($this->pending < 1 && $this->idlePeriod >= 0 && $this->connecting !== null) {
9393
$this->idleTimer = $this->loop->addTimer($this->idlePeriod, function () {
9494
$this->connecting->then(function (ConnectionInterface $connection) {
9595
$this->disconnecting = $connection;

tests/Io/LazyConnectionTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,29 @@ public function testPingWillRejectAndStartTimerWhenPingFromUnderlyingConnectionR
513513
$ret->then($this->expectCallableNever(), $this->expectCallableOnceWith($error));
514514
}
515515

516+
public function testPingWillRejectAndNotStartIdleTimerWhenPingFromUnderlyingConnectionRejectsBecauseConnectionIsDead()
517+
{
518+
$error = new \RuntimeException();
519+
520+
$base = $this->getMockBuilder('React\MySQL\Io\LazyConnection')->setMethods(array('ping', 'close'))->disableOriginalConstructor()->getMock();
521+
$base->expects($this->once())->method('ping')->willReturnCallback(function () use ($base, $error) {
522+
$base->emit('close');
523+
return \React\Promise\reject($error);
524+
});
525+
$base->expects($this->never())->method('close');
526+
527+
$factory = $this->getMockBuilder('React\MySQL\Factory')->disableOriginalConstructor()->getMock();
528+
$factory->expects($this->once())->method('createConnection')->willReturn(\React\Promise\resolve($base));
529+
530+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
531+
$loop->expects($this->never())->method('addTimer');
532+
533+
$connection = new LazyConnection($factory, '', $loop);
534+
535+
$ret = $connection->ping();
536+
$ret->then($this->expectCallableNever(), $this->expectCallableOnceWith($error));
537+
}
538+
516539
public function testQuitResolvesAndEmitsCloseImmediatelyWhenConnectionIsNotAlreadyPending()
517540
{
518541
$factory = $this->getMockBuilder('React\MySQL\Factory')->disableOriginalConstructor()->getMock();

0 commit comments

Comments
 (0)