Skip to content

Commit 88104bf

Browse files
authored
Merge pull request #31 from roadrunner-php/feature/1728
Dynamic Workers Scaling via RPC
2 parents 5c22fab + 23e2811 commit 88104bf

File tree

6 files changed

+377
-0
lines changed

6 files changed

+377
-0
lines changed

src/WorkerPool.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Spiral\RoadRunner;
6+
7+
use Spiral\Goridge\RPC\Codec\JsonCodec;
8+
use Spiral\Goridge\RPC\RPCInterface;
9+
10+
final class WorkerPool
11+
{
12+
private readonly RPCInterface $rpc;
13+
14+
public function __construct(
15+
RPCInterface $rpc,
16+
) {
17+
$this->rpc = $rpc->withCodec(new JsonCodec());
18+
}
19+
20+
/**
21+
* Add worker to the pool.
22+
*
23+
* @param non-empty-string $plugin
24+
*/
25+
public function addWorker(string $plugin): void
26+
{
27+
$this->rpc->call('informer.AddWorker', $plugin);
28+
}
29+
30+
/**
31+
* Remove worker from the pool.
32+
*
33+
* @param non-empty-string $plugin
34+
*/
35+
public function removeWorker(string $plugin): void
36+
{
37+
$this->rpc->call('informer.RemoveWorker', $plugin);
38+
}
39+
}

tests/Unit/EnvironmentTest.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Spiral\RoadRunner\Tests\Worker\Unit;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Spiral\RoadRunner\Environment;
9+
10+
final class EnvironmentTest extends TestCase
11+
{
12+
public function testGetModeWithDefault(): void
13+
{
14+
$env = new Environment();
15+
$this->assertEquals('', $env->getMode());
16+
}
17+
18+
public function testGetModeWithValue(): void
19+
{
20+
$env = new Environment(['RR_MODE' => 'mode_value']);
21+
$this->assertEquals('mode_value', $env->getMode());
22+
}
23+
24+
public function testGetRelayAddressWithDefault(): void
25+
{
26+
$env = new Environment();
27+
$this->assertEquals('pipes', $env->getRelayAddress());
28+
}
29+
30+
public function testGetRelayAddressWithValue(): void
31+
{
32+
$env = new Environment(['RR_RELAY' => 'relay_value']);
33+
$this->assertEquals('relay_value', $env->getRelayAddress());
34+
}
35+
36+
public function testGetRPCAddressWithDefault(): void
37+
{
38+
$env = new Environment();
39+
$this->assertEquals('tcp://127.0.0.1:6001', $env->getRPCAddress());
40+
}
41+
42+
public function testGetRPCAddressWithValue(): void
43+
{
44+
$env = new Environment(['RR_RPC' => 'rpc_value']);
45+
$this->assertEquals('rpc_value', $env->getRPCAddress());
46+
}
47+
48+
public function testFromGlobals(): void
49+
{
50+
$_ENV['RR_MODE'] = 'global_mode';
51+
$_SERVER['RR_RELAY'] = 'global_relay';
52+
53+
$env = Environment::fromGlobals();
54+
55+
$this->assertEquals('global_mode', $env->getMode());
56+
$this->assertEquals('global_relay', $env->getRelayAddress());
57+
$this->assertEquals('tcp://127.0.0.1:6001', $env->getRPCAddress());
58+
}
59+
}

tests/Unit/PayloadFactoryTest.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Spiral\RoadRunner\Tests\Worker\Unit;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Spiral\Goridge\Frame;
9+
use Spiral\RoadRunner\Exception\RoadRunnerException;
10+
use Spiral\RoadRunner\Message\Command\GetProcessId;
11+
use Spiral\RoadRunner\Message\Command\Pong;
12+
use Spiral\RoadRunner\Message\Command\StreamStop;
13+
use Spiral\RoadRunner\Message\Command\WorkerStop;
14+
use Spiral\RoadRunner\PayloadFactory;
15+
16+
final class PayloadFactoryTest extends TestCase
17+
{
18+
public function testFromFrameWithStopFlag(): void
19+
{
20+
$frame = new Frame("{}", []);
21+
$frame->byte10 = Frame::BYTE10_STOP;
22+
$payload = PayloadFactory::fromFrame($frame);
23+
24+
$this->assertInstanceOf(StreamStop::class, $payload);
25+
}
26+
27+
public function testFromFrameWithPongFlag(): void
28+
{
29+
$frame = new Frame("{}", []);
30+
$frame->byte10 = Frame::BYTE10_PONG;
31+
$payload = PayloadFactory::fromFrame($frame);
32+
33+
$this->assertInstanceOf(Pong::class, $payload);
34+
}
35+
36+
public function testFromFrameWithoutSpecificFlags(): void
37+
{
38+
$frame = new Frame("test", [0]);
39+
$payload = PayloadFactory::fromFrame($frame);
40+
41+
$this->assertNotNull($payload);
42+
$this->assertSame("test", $payload->body);
43+
$this->assertSame("", $payload->header);
44+
}
45+
46+
public function testMakeControlWithWorkerStop(): void
47+
{
48+
$json = \json_encode(['stop' => true]);
49+
$frame = new Frame($json);
50+
$frame->setFlag(Frame::CONTROL);
51+
52+
$payload = PayloadFactory::fromFrame($frame);
53+
$this->assertInstanceOf(WorkerStop::class, $payload);
54+
}
55+
56+
public function testMakeControlWithGetProcessId(): void
57+
{
58+
$json = \json_encode(['pid' => true]);
59+
$frame = new Frame($json);
60+
$frame->setFlag(Frame::CONTROL);
61+
62+
$payload = PayloadFactory::fromFrame($frame);
63+
$this->assertInstanceOf(GetProcessId::class, $payload);
64+
}
65+
66+
public function testFromFrameWithControlFlag(): void
67+
{
68+
$frame = new Frame(null, [], Frame::CONTROL);
69+
70+
$this->expectException(RoadRunnerException::class);
71+
$this->expectExceptionMessage('Invalid task header, JSON payload is expected: Syntax error');
72+
PayloadFactory::fromFrame($frame);
73+
}
74+
75+
public function testMakeControlWithException(): void
76+
{
77+
$this->expectException(RoadRunnerException::class);
78+
$this->expectExceptionMessage('Invalid task header, undefined control package');
79+
$json = json_encode([]);
80+
$frame = new Frame($json);
81+
$frame->setFlag(Frame::CONTROL);
82+
83+
PayloadFactory::fromFrame($frame);
84+
}
85+
86+
public function testMakePayload(): void
87+
{
88+
$this->markTestIncomplete('Not implemented yet.');
89+
}
90+
}

tests/Unit/PayloadTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Spiral\RoadRunner\Tests\Worker\Unit;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Spiral\RoadRunner\Payload;
9+
10+
final class PayloadTest extends TestCase
11+
{
12+
public function testPayloadConstructionWithValues(): void
13+
{
14+
$payload = new Payload('body_content', 'header_content', false);
15+
16+
$this->assertEquals('body_content', $payload->body);
17+
$this->assertEquals('header_content', $payload->header);
18+
$this->assertFalse($payload->eos);
19+
}
20+
21+
public function testPayloadConstructionWithDefaultValues(): void
22+
{
23+
$payload = new Payload(null, null);
24+
25+
$this->assertEquals('', $payload->body);
26+
$this->assertEquals('', $payload->header);
27+
$this->assertTrue($payload->eos);
28+
}
29+
30+
public function testPayloadConstructionWithPartialValues(): void
31+
{
32+
$payload = new Payload('body_content');
33+
34+
$this->assertEquals('body_content', $payload->body);
35+
$this->assertEquals('', $payload->header);
36+
$this->assertTrue($payload->eos);
37+
}
38+
39+
public function testPayloadConstructionWithEosFalse(): void
40+
{
41+
$payload = new Payload(null, null, false);
42+
43+
$this->assertEquals('', $payload->body);
44+
$this->assertEquals('', $payload->header);
45+
$this->assertFalse($payload->eos);
46+
}
47+
}

tests/Unit/VersionTest.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Spiral\RoadRunner\Tests\Worker\Unit;
6+
7+
use Composer\InstalledVersions;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
use Spiral\RoadRunner\Version;
11+
12+
final class VersionTest extends TestCase
13+
{
14+
public static function provideVersions(): iterable
15+
{
16+
yield [
17+
[
18+
'spiral/roadrunner' => [
19+
'pretty_version' => 'v1.9.0',
20+
],
21+
'spiral/roadrunner-worker' => [
22+
'pretty_version' => 'v1.8.0',
23+
],
24+
],
25+
'1.9.0',
26+
'1.*'
27+
];
28+
29+
30+
yield [
31+
[
32+
'spiral/roadrunner' => [
33+
'pretty_version' => '2.1.0',
34+
],
35+
],
36+
'2.1.0',
37+
'2.*'
38+
];
39+
40+
yield [
41+
[
42+
'spiral/roadrunner-worker' => [
43+
'pretty_version' => 'v1.8.0',
44+
],
45+
'spiral/roadrunner' => [
46+
'pretty_version' => 'v1.9.0',
47+
],
48+
],
49+
'1.9.0',
50+
'1.*'
51+
];
52+
53+
yield [
54+
[
55+
'spiral/roadrunner-worker' => [
56+
'pretty_version' => 'v1.8.0',
57+
],
58+
],
59+
'1.8.0',
60+
'1.*'
61+
];
62+
63+
yield [
64+
[
65+
'spiral/roadrunner-http' => [
66+
'pretty_version' => 'v1.8.0',
67+
],
68+
],
69+
Version::VERSION_FALLBACK,
70+
'*'
71+
];
72+
73+
yield [
74+
[],
75+
Version::VERSION_FALLBACK,
76+
'*'
77+
];
78+
}
79+
80+
protected function setUp(): void
81+
{
82+
parent::setUp();
83+
84+
$ref = new \ReflectionClass(InstalledVersions::class);
85+
$ref->setStaticPropertyValue('canGetVendors', false);
86+
}
87+
88+
#[DataProvider('provideVersions')]
89+
public function testGetVersion(array $versions, string $expectedVersion, string $expectedConstraint): void
90+
{
91+
InstalledVersions::reload([
92+
'versions' => $versions,
93+
]);
94+
95+
$this->assertSame($expectedVersion, Version::current());
96+
$this->assertSame($expectedConstraint, Version::constraint());
97+
}
98+
}

tests/Unit/WorkerPoolTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Spiral\RoadRunner\Tests\Worker\Unit;
6+
7+
use PHPUnit\Framework\MockObject\Exception;
8+
use PHPUnit\Framework\TestCase;
9+
use Spiral\Goridge\RPC\Codec\JsonCodec;
10+
use Spiral\Goridge\RPC\RPCInterface;
11+
use Spiral\RoadRunner\WorkerPool;
12+
13+
final class WorkerPoolTest extends TestCase
14+
{
15+
private \PHPUnit\Framework\MockObject\MockObject|RPCInterface $rpc;
16+
private WorkerPool $workerPool;
17+
18+
/**
19+
* @throws Exception
20+
*/
21+
protected function setUp(): void
22+
{
23+
parent::setUp();
24+
25+
$this->rpc = $this->createMock(RPCInterface::class);
26+
$this->rpc->expects($this->once())->method('withCodec')->with($this->isInstanceOf(JsonCodec::class))->willReturnSelf();
27+
28+
$this->workerPool = new WorkerPool($this->rpc);
29+
}
30+
31+
public function testAddWorker(): void
32+
{
33+
$this->rpc->expects($this->once())->method('call')->with('informer.AddWorker', 'test');
34+
35+
$this->workerPool->addWorker('test');
36+
}
37+
38+
public function testRemoveWorker(): void
39+
{
40+
$this->rpc->expects($this->once())->method('call')->with('informer.RemoveWorker', 'test');
41+
42+
$this->workerPool->removeWorker('test');
43+
}
44+
}

0 commit comments

Comments
 (0)