blob: 01e45325bc8a3d17730971f8fa9f035d7b5d956c [file] [log] [blame]
<?php
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
namespace Test\Thrift\Unit\Lib\Transport;
use phpmock\phpunit\PHPMock;
use PHPUnit\Framework\TestCase;
use Thrift\Exception\TException;
use Thrift\Transport\TSocketPool;
class TSocketPoolTest extends TestCase
{
use PHPMock;
protected function setUp(): void
{
#need to be defined before the TSocketPool class definition
self::defineFunctionMock('Thrift\Transport', 'function_exists');
}
/**
* @dataProvider constructDataProvider
*/
public function testConstruct(
$hosts,
$ports,
$persist,
$debugHandler,
$expectedServers
) {
$socketPool = new TSocketPool($hosts, $ports, $persist, $debugHandler);
$ref = new \ReflectionObject($socketPool);
$serversProp = $ref->getProperty('servers_');
$serversProp->setAccessible(true);
$this->assertEquals($expectedServers, $serversProp->getValue($socketPool));
}
public function constructDataProvider()
{
yield 'one server' => [
['localhost'],
[9090],
false,
null,
[
['host' => 'localhost', 'port' => 9090],
],
];
yield 'two servers' => [
['localhost1', 'localhost2'],
[9090, 9091],
false,
null,
[
['host' => 'localhost1', 'port' => 9090],
['host' => 'localhost2', 'port' => 9091],
],
];
yield 'one server with one port' => [
['localhost'],
9090,
false,
null,
[
['host' => 'localhost', 'port' => 9090],
],
];
yield 'two servers with one port' => [
['localhost1', 'localhost2'],
9090,
false,
null,
[
['host' => 'localhost1', 'port' => 9090],
['host' => 'localhost2', 'port' => 9090],
],
];
}
public function testAddServer(): void
{
$socketPool = new TSocketPool([], []);
$socketPool->addServer('localhost', 9090);
$ref = new \ReflectionObject($socketPool);
$servers = $ref->getProperty('servers_');
$servers->setAccessible(true);
$this->assertEquals([['host' => 'localhost', 'port' => 9090]], $servers->getValue($socketPool));
}
public function testSetNumRetries(): void
{
$socketPool = new TSocketPool([], []);
$socketPool->setNumRetries(5);
$ref = new \ReflectionObject($socketPool);
$numRetries = $ref->getProperty('numRetries_');
$numRetries->setAccessible(true);
$this->assertEquals(5, $numRetries->getValue($socketPool));
}
public function testrSetRetryInterval(): void
{
$socketPool = new TSocketPool([], []);
$socketPool->setRetryInterval(5);
$ref = new \ReflectionObject($socketPool);
$retryInterval = $ref->getProperty('retryInterval_');
$retryInterval->setAccessible(true);
$this->assertEquals(5, $retryInterval->getValue($socketPool));
}
public function testrSetMaxConsecutiveFailures(): void
{
$socketPool = new TSocketPool([], []);
$socketPool->setMaxConsecutiveFailures(5);
$ref = new \ReflectionObject($socketPool);
$maxConsecutiveFailures = $ref->getProperty('maxConsecutiveFailures_');
$maxConsecutiveFailures->setAccessible(true);
$this->assertEquals(5, $maxConsecutiveFailures->getValue($socketPool));
}
public function testrSetRandomize(): void
{
$socketPool = new TSocketPool([], []);
$socketPool->setRandomize(false);
$ref = new \ReflectionObject($socketPool);
$randomize = $ref->getProperty('randomize_');
$randomize->setAccessible(true);
$this->assertEquals(false, $randomize->getValue($socketPool));
}
public function testrSetAlwaysTryLast(): void
{
$socketPool = new TSocketPool([], []);
$socketPool->setAlwaysTryLast(false);
$ref = new \ReflectionObject($socketPool);
$alwaysTryLast = $ref->getProperty('alwaysTryLast_');
$alwaysTryLast->setAccessible(true);
$this->assertEquals(false, $alwaysTryLast->getValue($socketPool));
}
/**
* @dataProvider openDataProvider
*/
public function testOpen(
$hosts,
$ports,
$persist,
$debugHandler,
$randomize,
$retryInterval,
$numRetries,
$maxConsecutiveFailures,
$debug,
$servers,
$functionExistCallParams,
$functionExistResult,
$apcuFetchCallParams,
$apcuFetchResult,
$timeResult,
$debugHandlerCall,
$apcuStoreCallParams,
$fsockopenCallParams,
$fsockopenResult,
$expectedException,
$expectedExceptionMessage
) {
$this->getFunctionMock('Thrift\Transport', 'function_exists')
->expects($this->exactly(count($functionExistCallParams)))
->withConsecutive(...$functionExistCallParams)
->willReturnOnConsecutiveCalls(...$functionExistResult);
$this->getFunctionMock('Thrift\Transport', 'shuffle')
->expects($randomize ? $this->once() : $this->never())
->with($servers)
->willReturnCallback(function (array &$servers) {
$servers = array_reverse($servers);
return true;
});
$this->getFunctionMock('Thrift\Transport', 'apcu_fetch')
->expects($this->exactly(count($apcuFetchCallParams)))
->withConsecutive(...$apcuFetchCallParams)
->willReturnOnConsecutiveCalls(...$apcuFetchResult);
$this->getFunctionMock('Thrift\Transport', 'call_user_func')
->expects($this->exactly(count($debugHandlerCall)))
->withConsecutive(...$debugHandlerCall)
->willReturn(true);
$this->getFunctionMock('Thrift\Transport', 'apcu_store')
->expects($this->exactly(count($apcuStoreCallParams)))
->withConsecutive(...$apcuStoreCallParams)
->willReturn(true);
$this->getFunctionMock('Thrift\Transport', 'time')
->expects($this->exactly(count($timeResult)))
->willReturnOnConsecutiveCalls(...$timeResult);
#due to the running tests in separate process we could not open stream in data provider, so we need to do it here
foreach ($fsockopenResult as $num => $result) {
$fsockopenResult[$num] = $result ? fopen(...$result) : $result;
}
$this->getFunctionMock('Thrift\Transport', $persist ? 'pfsockopen' : 'fsockopen')
->expects($this->exactly(count($fsockopenCallParams)))
->withConsecutive(...$fsockopenCallParams)
->willReturnOnConsecutiveCalls(...$fsockopenResult);
$this->getFunctionMock('Thrift\Transport', 'socket_import_stream')
->expects(is_null($expectedException) ? $this->once() : $this->never())
->with(
$this->callback(function ($stream) {
return is_resource($stream);
})
)
->willReturn(true);
$this->getFunctionMock('Thrift\Transport', 'socket_set_option')
->expects(is_null($expectedException) ? $this->once() : $this->never())
->with(
$this->anything(), #$socket,
SOL_TCP, #$level
TCP_NODELAY, #$option
1 #$value
)
->willReturn(true);
if ($expectedException) {
$this->expectException($expectedException);
$this->expectExceptionMessage($expectedExceptionMessage);
}
$socketPool = new TSocketPool($hosts, $ports, $persist, $debugHandler);
$socketPool->setRandomize($randomize);
$socketPool->setRetryInterval($retryInterval);
$socketPool->setNumRetries($numRetries);
$socketPool->setMaxConsecutiveFailures($maxConsecutiveFailures);
$socketPool->setDebug($debug);
$this->assertNull($socketPool->open());
}
public function openDataProvider()
{
$default = [
'hosts' => ['localhost'],
'ports' => [9090],
'persist' => false,
'debugHandler' => null,
'randomize' => true,
'retryInterval' => 5,
'numRetries' => 1,
'maxConsecutiveFailures' => 1,
'debug' => false,
'servers' => [
['host' => 'localhost', 'port' => 9090],
],
'functionExistCallParams' => [
['apcu_fetch'],
['socket_import_stream'],
['socket_set_option'],
],
'functionExistResult' => [
true,
true,
true,
],
'apcuFetchCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
],
'apcuFetchResult' => [
false,
],
'timeResult' => [],
'debugHandlerCall' => [],
'apcuStoreCallParams' => [],
'fsockopenCallParams' => [
[
'localhost',
9090,
$this->anything(), #$errno,
$this->anything(), #$errstr,
$this->anything(), #$this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
],
],
'fsockopenResult' => [
['php://temp', 'r'],
],
'expectedException' => null,
'expectedExceptionMessage' => null,
];
yield 'one server ready' => $default;
yield 'one server failed' => array_merge(
$default,
[
'functionExistCallParams' => [
['apcu_fetch'],
],
'fsockopenResult' => [
false,
],
'apcuFetchCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
['thrift_consecfails:localhost:9090~', $this->anything()],
],
'apcuStoreCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
['thrift_consecfails:localhost:9090~', $this->anything(), 0],
],
'timeResult' => [
1,
],
'expectedException' => TException::class,
'expectedExceptionMessage' => 'TSocketPool: All hosts in pool are down. (localhost:9090)',
]
);
yield 'connect to one server on second attempt' => array_merge(
$default,
[
'numRetries' => 2,
'fsockopenCallParams' => [
[
'localhost',
9090,
$this->anything(), #$errno,
$this->anything(), #$errstr,
$this->anything(), #$this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
],
[
'localhost',
9090,
$this->anything(), #$errno,
$this->anything(), #$errstr,
$this->anything(), #$this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
],
],
'fsockopenResult' => [
false,
['php://temp', 'r'],
],
'apcuStoreCallParams' => [],
]
);
yield 'last time fail time is not expired' => array_merge(
$default,
[
'retryInterval' => 5,
'apcuFetchResult' => [
99,
],
'apcuStoreCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
],
'timeResult' => [
100,
],
]
);
yield 'last time fail time is expired, store info to debug' => array_merge(
$default,
[
'retryInterval' => 5,
'apcuFetchResult' => [
90,
],
'apcuStoreCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
],
'timeResult' => [
100,
],
'debug' => true,
'debugHandlerCall' => [
['error_log', 'TSocketPool: retryInterval (5) has passed for host localhost:9090'],
],
]
);
yield 'not accessible server, store info to debug' => array_merge(
$default,
[
'retryInterval' => 5,
'functionExistCallParams' => [
['apcu_fetch'],
],
'functionExistResult' => [
true,
],
'apcuFetchCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
['thrift_consecfails:localhost:9090~', $this->anything()],
],
'apcuFetchResult' => [
90,
],
'apcuStoreCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
['thrift_consecfails:localhost:9090~', 0],
],
'timeResult' => [
100,
101,
],
'fsockopenResult' => [
false,
],
'debug' => true,
'debugHandlerCall' => [
['error_log', 'TSocketPool: retryInterval (5) has passed for host localhost:9090'],
['error_log', 'TSocket: Could not connect to localhost:9090 ( [])'],
['error_log', 'TSocketPool: marking localhost:9090 as down for 5 secs after 1 failed attempts.'],
['error_log', 'TSocketPool: All hosts in pool are down. (localhost:9090)'],
],
'expectedException' => TException::class,
'expectedExceptionMessage' => 'TSocketPool: All hosts in pool are down. (localhost:9090)',
]
);
yield 'max consecutive failures' => array_merge(
$default,
[
'maxConsecutiveFailures' => 5,
'functionExistCallParams' => [
['apcu_fetch'],
],
'functionExistResult' => [
true,
],
'apcuFetchCallParams' => [
['thrift_failtime:localhost:9090~', $this->anything()],
['thrift_consecfails:localhost:9090~', $this->anything()],
],
'apcuStoreCallParams' => [
['thrift_consecfails:localhost:9090~', 1],
],
'timeResult' => [],
'fsockopenResult' => [
false,
],
'expectedException' => TException::class,
'expectedExceptionMessage' => 'TSocketPool: All hosts in pool are down. (localhost:9090)',
]
);
yield 'apcu disabled' => array_merge(
$default,
[
'functionExistCallParams' => [
['apcu_fetch'],
],
'functionExistResult' => [
false,
],
'fsockopenResult' => [
false,
],
'timeResult' => [
1,
],
'apcuFetchCallParams' => [],
'apcuStoreCallParams' => [],
'expectedException' => TException::class,
'expectedExceptionMessage' => 'TSocketPool: All hosts in pool are down. (localhost:9090)',
]
);
yield 'second host accessible' => array_merge(
$default,
[
'hosts' => ['host1', 'host2'],
'ports' => [9090, 9091],
'servers' => [
['host' => 'host1', 'port' => 9090],
['host' => 'host2', 'port' => 9091],
],
'fsockopenCallParams' => [
[
'host2',
9091,
$this->anything(), #$errno,
$this->anything(), #$errstr,
$this->anything(), #$this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
],
[
'host1',
9090,
$this->anything(), #$errno,
$this->anything(), #$errstr,
$this->anything(), #$this->sendTimeoutSec_ + ($this->sendTimeoutUsec_ / 1000000),
],
],
'fsockopenResult' => [
false,
['php://temp', 'r'],
],
'apcuFetchCallParams' => [
['thrift_failtime:host2:9091~', $this->anything()],
['thrift_consecfails:host2:9091~', $this->anything()],
['thrift_failtime:host1:9090~', $this->anything()],
],
'apcuStoreCallParams' => [
['thrift_failtime:host2:9091~', $this->anything()],
['thrift_consecfails:host2:9091~', $this->anything(), 0],
],
'timeResult' => [
1,
],
]
);
}
}