diff --git a/composer.json b/composer.json index bf521e8..d7bcb0d 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "thesis/googleapis-rpc-types": "^0.1.6", "thesis/package-version": "^0.1.2", "thesis/protobuf": "^0.1.8", - "thesis/protobuf-known-types": "^0.1.5" + "thesis/protobuf-known-types": "^0.1.7" }, "require-dev": { "ext-bcmath": "*", @@ -53,7 +53,12 @@ "type": "path", "url": "packages/*", "options": { - "symlink": true + "symlink": true, + "versions": { + "thesis/grpc-client": "0.1.x-dev", + "thesis/grpc-protocol": "0.1.x-dev", + "thesis/grpc-server": "0.1.x-dev" + } } } ], diff --git a/packages/client/src/Client/Builder.php b/packages/client/src/Client/Builder.php index 0d95e0f..260fdc1 100644 --- a/packages/client/src/Client/Builder.php +++ b/packages/client/src/Client/Builder.php @@ -9,6 +9,7 @@ use Amp\Http\Client\DelegateHttpClient; use Amp\Http\Client\HttpClientBuilder; use Amp\Socket\ConnectContext; +use Amp\Socket\DnsSocketConnector; use Amp\Socket\SocketConnector; use Thesis\Grpc\Client; use Thesis\Grpc\Client\Internal\Connection; @@ -62,6 +63,8 @@ final class Builder private ?LoadBalancerFactory $loadBalancerFactory = null; + private ?Retry $retry = null; + /** @var \SplObjectStorage */ private \SplObjectStorage $endpointResolvers; @@ -186,6 +189,14 @@ public function withLoadBalancer(LoadBalancerFactory $factory): self return $builder; } + public function withRetry(Retry $policy): self + { + $builder = clone $this; + $builder->retry = $policy; + + return $builder; + } + public function withEndpointResolver(Scheme $scheme, EndpointResolver $resolver): self { $builder = clone $this; @@ -207,6 +218,7 @@ public function build(): Client $compressor = $this->compressor ?? IdentityCompressor::Compressor; $protobuf = $this->protobuf ?? Decoder\Builder::buildDefault(); $loadBalancerFactory = $this->loadBalancerFactory ?? new LoadBalancer\PickFirstFactory(); + $retry = $this->retry ?? Retry::disabled(); $tlsContext = $this->credentials?->createContext(); $uriFactory = new Http2\UriFactory($tlsContext !== null ? Internal\HttpScheme::Https : Internal\HttpScheme::Http); $transferTimeout = $this->transferTimeout; @@ -230,7 +242,7 @@ public function build(): Client ->usingPool(ConnectionLimitingPool::byAuthority( $this->connectionLimit, new DefaultConnectionFactory( - $this->connector, + $this->connector ?? new DnsSocketConnector(), new ConnectContext() ->withConnectTimeout($this->connectTimeout) ->withTlsContext($tlsContext), @@ -257,6 +269,7 @@ public function build(): Client encoder: $encoder, compressor: $compressor, ), + retry: $retry, ), ), ); diff --git a/packages/client/src/Client/Internal/AmphpHttpClient.php b/packages/client/src/Client/Internal/AmphpHttpClient.php index c56728a..b751c67 100644 --- a/packages/client/src/Client/Internal/AmphpHttpClient.php +++ b/packages/client/src/Client/Internal/AmphpHttpClient.php @@ -26,16 +26,12 @@ public function invoke( Metadata $md = new Metadata(), Cancellation $cancellation = new NullCancellation(), ): object { - $stream = $this->connection->createStream( + return $this->connection->invoke( + $request, $invoke, $md, $cancellation, ); - - $stream->send($request); - $stream->close(); - - return $stream->receive(); } #[\Override] diff --git a/packages/client/src/Client/Internal/Connection.php b/packages/client/src/Client/Internal/Connection.php index 884fb26..e516f8c 100644 --- a/packages/client/src/Client/Internal/Connection.php +++ b/packages/client/src/Client/Internal/Connection.php @@ -8,6 +8,7 @@ use Amp\NullCancellation; use Thesis\Grpc\Client\Invoke; use Thesis\Grpc\ClientStream; +use Thesis\Grpc\InvokeError; use Thesis\Grpc\Metadata; /** @@ -15,6 +16,21 @@ */ interface Connection { + /** + * @template In of object + * @template Out of object + * @param In $request + * @param Invoke $invoke + * @return Out + * @throws InvokeError + */ + public function invoke( + object $request, + Invoke $invoke, + Metadata $md = new Metadata(), + Cancellation $cancellation = new NullCancellation(), + ): object; + /** * @template In of object * @template Out of object diff --git a/packages/client/src/Client/Internal/Connection/DefaultConnection.php b/packages/client/src/Client/Internal/Connection/DefaultConnection.php index 61da9fb..133fa9a 100644 --- a/packages/client/src/Client/Internal/Connection/DefaultConnection.php +++ b/packages/client/src/Client/Internal/Connection/DefaultConnection.php @@ -7,6 +7,7 @@ use Amp\Cancellation; use Amp\DeferredCancellation; use Amp\NullCancellation; +use Thesis\Grpc\Client\Endpoint; use Thesis\Grpc\Client\EndpointResolver; use Thesis\Grpc\Client\EndpointResolverListener; use Thesis\Grpc\Client\Internal\Connection; @@ -17,6 +18,7 @@ use Thesis\Grpc\Client\LoadBalancerFactory; use Thesis\Grpc\Client\PickContext; use Thesis\Grpc\Client\Resolution; +use Thesis\Grpc\Client\Retry; use Thesis\Grpc\Client\Target; use Thesis\Grpc\ClientStream; use Thesis\Grpc\Metadata; @@ -38,6 +40,7 @@ public function __construct( LoadBalancerFactory $loadBalancerFactory, private InterceptorComposer $interceptor, private StreamFactory $streams, + private Retry $retry, ) { $this->deferredCancellation = new DeferredCancellation(); @@ -51,26 +54,47 @@ public function __construct( } #[\Override] - public function createStream( + public function invoke( + object $request, Invoke $invoke, Metadata $md = new Metadata(), Cancellation $cancellation = new NullCancellation(), - ): ClientStream { - $endpoint = $this->balancer->pick(new PickContext($invoke->method, $md)); + ): object { + /** @var list $excluded */ + $excluded = []; - return $this->interceptor->intercept( // @phpstan-ignore return.type - $invoke, - $md, - $cancellation, - fn(Invoke $invoke, Metadata $md, Cancellation $cancellation) => $this->streams->create( + return $this->retry->call( + function () use ( + &$excluded, + $request, $invoke, - $endpoint->address, $md, $cancellation, - ), + ): object { + $endpoint = $this->balancer->pick(new PickContext($invoke->method, $md, $excluded)); + $excluded[] = $endpoint; + + $stream = $this->stream($endpoint, $invoke, $md, $cancellation); + $stream->send($request); + $stream->close(); + + return $stream->receive(); + }, + $cancellation, ); } + #[\Override] + public function createStream( + Invoke $invoke, + Metadata $md = new Metadata(), + Cancellation $cancellation = new NullCancellation(), + ): ClientStream { + $endpoint = $this->balancer->pick(new PickContext($invoke->method, $md)); + + return $this->stream($endpoint, $invoke, $md, $cancellation); + } + #[\Override] public function close(Cancellation $cancellation = new NullCancellation()): void { @@ -84,4 +108,29 @@ public function onResolve(Resolution|\Throwable $result): void $this->balancer->refresh($result->endpoints); } } + + /** + * @template In of object + * @template Out of object + * @param Invoke $invoke + * @return ClientStream + */ + private function stream( + Endpoint $endpoint, + Invoke $invoke, + Metadata $md, + Cancellation $cancellation, + ): ClientStream { + return $this->interceptor->intercept( // @phpstan-ignore return.type + $invoke, + $md, + $cancellation, + fn(Invoke $invoke, Metadata $md, Cancellation $cancellation) => $this->streams->create( + $invoke, + $endpoint->address, + $md, + $cancellation, + ), + ); + } } diff --git a/packages/client/src/Client/Internal/Connection/LazyConnection.php b/packages/client/src/Client/Internal/Connection/LazyConnection.php index 5607c82..31d7bf2 100644 --- a/packages/client/src/Client/Internal/Connection/LazyConnection.php +++ b/packages/client/src/Client/Internal/Connection/LazyConnection.php @@ -28,16 +28,27 @@ public function __construct( private readonly \Closure $factory, ) {} + #[\Override] + public function invoke( + object $request, + Invoke $invoke, + Metadata $md = new Metadata(), + Cancellation $cancellation = new NullCancellation(), + ): object { + return $this + ->createConnection($cancellation) + ->invoke($request, $invoke, $md, $cancellation); + } + #[\Override] public function createStream( Invoke $invoke, Metadata $md = new Metadata(), Cancellation $cancellation = new NullCancellation(), ): ClientStream { - $this->future ??= async($this->factory); - $connection = $this->future->await($cancellation); - - return $connection->createStream($invoke, $md, $cancellation); + return $this + ->createConnection($cancellation) + ->createStream($invoke, $md, $cancellation); } #[\Override] @@ -48,4 +59,9 @@ public function close(Cancellation $cancellation = new NullCancellation()): void $future?->await($cancellation)->close($cancellation); } + + private function createConnection(Cancellation $cancellation): Connection + { + return ($this->future ??= async($this->factory))->await($cancellation); + } } diff --git a/packages/client/src/Client/Internal/Http2/ConcurrentClientStream.php b/packages/client/src/Client/Internal/Http2/ConcurrentClientStream.php index 49da0ac..7b0b69e 100644 --- a/packages/client/src/Client/Internal/Http2/ConcurrentClientStream.php +++ b/packages/client/src/Client/Internal/Http2/ConcurrentClientStream.php @@ -40,7 +40,10 @@ public function __construct( private readonly \Closure $decode, private readonly ErrorHandler $errors, private readonly Future $complete, - ) {} + ) { + $responseFuture->ignore(); + $complete->ignore(); + } #[\Override] public function send(object $message): void diff --git a/packages/client/src/Client/LoadBalancer/PickFirst.php b/packages/client/src/Client/LoadBalancer/PickFirst.php index 7ff7591..51f52ef 100644 --- a/packages/client/src/Client/LoadBalancer/PickFirst.php +++ b/packages/client/src/Client/LoadBalancer/PickFirst.php @@ -20,7 +20,7 @@ final class PickFirst implements LoadBalancer * @param non-empty-list $endpoints */ public function __construct( - array $endpoints, + private array $endpoints, private readonly Randomizer $randomizer, ) { $this->current = $this->doPick($endpoints); @@ -29,12 +29,20 @@ public function __construct( #[\Override] public function refresh(array $endpoints): void { + $this->endpoints = $endpoints; $this->current = $this->doPick($endpoints, $this->current); } #[\Override] public function pick(PickContext $context): Endpoint { + foreach ([$this->current, ...$this->endpoints] as $endpoint) { + if (!$context->excluded($endpoint)) { + $this->current = $endpoint; + break; + } + } + return $this->current; } diff --git a/packages/client/src/Client/LoadBalancer/RoundRobin.php b/packages/client/src/Client/LoadBalancer/RoundRobin.php index c08dea0..b7c60bd 100644 --- a/packages/client/src/Client/LoadBalancer/RoundRobin.php +++ b/packages/client/src/Client/LoadBalancer/RoundRobin.php @@ -37,6 +37,14 @@ public function refresh(array $endpoints): void #[\Override] public function pick(PickContext $context): Endpoint { + for ($i = 0; $i < $this->count; ++$i) { + $endpoint = $this->endpoints[$this->cursor++ % $this->count]; // @phpstan-ignore offsetAccess.notFound + + if (!$context->excluded($endpoint)) { + return $endpoint; + } + } + return $this->endpoints[$this->cursor++ % $this->count]; // @phpstan-ignore offsetAccess.notFound } } diff --git a/packages/client/src/Client/PickContext.php b/packages/client/src/Client/PickContext.php index e6666d9..edb2405 100644 --- a/packages/client/src/Client/PickContext.php +++ b/packages/client/src/Client/PickContext.php @@ -13,9 +13,16 @@ { /** * @param non-empty-string $methodName + * @param list $excluded endpoints that already failed this call and should be skipped if possible */ public function __construct( public string $methodName, public Metadata $metadata, + private array $excluded = [], ) {} + + public function excluded(Endpoint $endpoint): bool + { + return array_any($this->excluded, $endpoint->equals(...)); + } } diff --git a/packages/client/src/Client/Retry.php b/packages/client/src/Client/Retry.php new file mode 100644 index 0000000..018a15d --- /dev/null +++ b/packages/client/src/Client/Retry.php @@ -0,0 +1,102 @@ + $retryableStatusCodes gRPC statuses that trigger a retry + */ + public function __construct( + private int $maxAttempts = 3, + private float $initialBackoff = 0.05, + private float $maxBackoff = 1.0, + private float $backoffMultiplier = 2.0, + private array $retryableStatusCodes = [ + Code::UNAVAILABLE, + ], + private Randomizer $randomizer = new Randomizer(), + ) {} + + public static function disabled(): self + { + return new self(maxAttempts: 1); + } + + /** + * @template T + * @param \Closure(): T $call + * @return T + * @throws GrpcException + */ + public function call(\Closure $call, Cancellation $cancellation): mixed + { + $error = null; + + for ($attempt = 1; $attempt <= $this->maxAttempts; ++$attempt) { + if ($attempt > 1) { + $this->backoff($attempt - 1, $cancellation); + } + + try { + return $call(); + } catch (\Throwable $e) { + if (!$this->shouldRetry($e)) { + throw self::createException($e); + } + + $error = $e; + } + } + + throw self::createException($error ?? new InvokeError(Code::UNAVAILABLE, 'no attempts were made')); + } + + private function shouldRetry(\Throwable $e): bool + { + if ($e instanceof InvokeError) { + return \in_array($e->statusCode, $this->retryableStatusCodes, true); + } + + return $e instanceof HttpException; + } + + /** + * @param positive-int $attempt + */ + private function backoff(int $attempt, Cancellation $cancellation): void + { + $ceiling = min($this->maxBackoff, $this->initialBackoff * $this->backoffMultiplier ** ($attempt - 1)); + + if ($ceiling > 0.0) { + delay($this->randomizer->getFloat(0.0, $ceiling), cancellation: $cancellation); + } + } + + private static function createException(\Throwable $e): GrpcException + { + if ($e instanceof InvokeError || $e instanceof ClientStreamIsClosed) { + return $e; + } + + return new InvokeError(Code::UNAVAILABLE, $e->getMessage(), previous: $e); + } +} diff --git a/packages/grpc/src/InvokeError.php b/packages/grpc/src/InvokeError.php index 73d6b25..67ac669 100644 --- a/packages/grpc/src/InvokeError.php +++ b/packages/grpc/src/InvokeError.php @@ -18,11 +18,15 @@ public function __construct( public readonly Code $statusCode, public readonly ?string $statusMessage = null, public readonly array $details = [], + ?\Throwable $previous = null, ) { - parent::__construct(\sprintf( - 'A grpc error with status code "%s" and message "%s" occurred', - $statusCode->name, - $statusMessage ?? '', - )); + parent::__construct( + \sprintf( + 'A grpc error with status code "%s" and message "%s" occurred', + $statusCode->name, + $statusMessage ?? '', + ), + previous: $previous, + ); } } diff --git a/tests/Client/LoadBalancer/PickFirstTest.php b/tests/Client/LoadBalancer/PickFirstTest.php index d1a48d4..c4fe524 100644 --- a/tests/Client/LoadBalancer/PickFirstTest.php +++ b/tests/Client/LoadBalancer/PickFirstTest.php @@ -118,6 +118,34 @@ public static function provideRefreshSwitchesPinnedEndpointCases(): iterable ]; } + public function testPickFailsOverToALiveEndpointWhenPinnedIsExcluded(): void + { + $a = new Endpoint(new Address('10.0.0.1:50051')); + $b = new Endpoint(new Address('10.0.0.2:50051')); + $c = new Endpoint(new Address('10.0.0.3:50051')); + + $balancer = new PickFirstFactory()->create([$a, $b, $c]); + $pinned = $balancer->pick(self::context()); + + $failedOver = $balancer->pick(new PickContext('/test.Service/Method', new Metadata(), [$pinned])); + + self::assertFalse($pinned->equals($failedOver)); + self::assertTrue($failedOver->equals($balancer->pick(self::context()))); + } + + public function testPickStaysPinnedWhenEveryEndpointIsExcluded(): void + { + $a = new Endpoint(new Address('10.0.0.1:50051')); + $b = new Endpoint(new Address('10.0.0.2:50051')); + + $balancer = new PickFirstFactory()->create([$a, $b]); + $pinned = $balancer->pick(self::context()); + + $picked = $balancer->pick(new PickContext('/test.Service/Method', new Metadata(), [$a, $b])); + + self::assertTrue($pinned->equals($picked)); + } + private static function context(): PickContext { return new PickContext('/test.Service/Method', new Metadata()); diff --git a/tests/Client/LoadBalancer/RoundRobinTest.php b/tests/Client/LoadBalancer/RoundRobinTest.php index 1c0fd4d..e8f0494 100644 --- a/tests/Client/LoadBalancer/RoundRobinTest.php +++ b/tests/Client/LoadBalancer/RoundRobinTest.php @@ -104,6 +104,33 @@ public static function provideRefreshCases(): iterable ]; } + public function testPickSkipsExcludedEndpoints(): void + { + $a = new Endpoint(new Address('10.0.0.1:50051')); + $b = new Endpoint(new Address('10.0.0.2:50051')); + $c = new Endpoint(new Address('10.0.0.3:50051')); + + $balancer = new RoundRobinFactory()->create([$a, $b, $c]); + + $context = new PickContext('/test.Service/Method', new Metadata(), [$a, $b]); + + for ($i = 0; $i < 5; ++$i) { + self::assertTrue($c->equals($balancer->pick($context))); + } + } + + public function testPickFallsBackWhenEveryEndpointIsExcluded(): void + { + $a = new Endpoint(new Address('10.0.0.1:50051')); + $b = new Endpoint(new Address('10.0.0.2:50051')); + + $balancer = new RoundRobinFactory()->create([$a, $b]); + + $picked = $balancer->pick(new PickContext('/test.Service/Method', new Metadata(), [$a, $b])); + + self::assertTrue($a->equals($picked) || $b->equals($picked)); + } + private static function context(): PickContext { return new PickContext('/test.Service/Method', new Metadata()); diff --git a/tests/RetryFailoverTest.php b/tests/RetryFailoverTest.php new file mode 100644 index 0000000..574c001 --- /dev/null +++ b/tests/RetryFailoverTest.php @@ -0,0 +1,106 @@ +server = new Server\Builder() + ->withAddresses('0.0.0.0:50052') + ->withServices(new EchoServiceServerRegistry(new SucceedingEchoServer())) + ->build(); + + $this->server->start(); + } + + protected function tearDown(): void + { + $this->server->stop(); + } + + public function testRetryFailsOverFromADeadEndpointToALiveOne(): void + { + $client = new EchoServiceClient( + new Client\Builder() + ->withHost(\sprintf('ipv4:%s,%s', self::DEAD, self::LIVE)) + ->withLoadBalancer(new RoundRobinFactory()) + ->withRetry(new Retry(maxAttempts: 3, initialBackoff: 0.0)) + ->build(), + ); + + for ($i = 0; $i < 10; ++$i) { + self::assertSame('ok', $client->echo(new EchoRequest('ping'))->sentence); + } + } + + public function testWithoutRetryADeadEndpointSurfacesAsUnavailable(): void + { + $client = new EchoServiceClient( + new Client\Builder() + ->withHost(\sprintf('ipv4:%s', self::DEAD)) + ->build(), + ); + + try { + $client->echo(new EchoRequest('ping')); + self::fail('Expected an InvokeError for the dead endpoint.'); + } catch (InvokeError $e) { + self::assertSame(Code::UNAVAILABLE, $e->statusCode); + } + } + + public function testRetryDoesNotHelpWhenEveryEndpointIsDead(): void + { + $client = new EchoServiceClient( + new Client\Builder() + ->withHost(\sprintf('ipv4:%s,127.0.0.1:50053', self::DEAD)) + ->withLoadBalancer(new RoundRobinFactory()) + ->withRetry(new Retry(maxAttempts: 3, initialBackoff: 0.0)) + ->build(), + ); + + $this->expectException(InvokeError::class); + $client->echo(new EchoRequest('ping')); + } +} + +/** + * @internal + */ +final readonly class SucceedingEchoServer implements EchoServiceServer +{ + #[\Override] + public function echo(EchoRequest $request, Metadata $md, Cancellation $cancellation): EchoResponse + { + return new EchoResponse('ok'); + } +} diff --git a/tests/genproto/Chat/Api/V1/Message.php b/tests/genproto/Chat/Api/V1/Message.php index c0c78d1..a87199e 100644 --- a/tests/genproto/Chat/Api/V1/Message.php +++ b/tests/genproto/Chat/Api/V1/Message.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/chat_v1.proto */ diff --git a/tests/genproto/Chat/Api/V1/MessengerServiceClient.php b/tests/genproto/Chat/Api/V1/MessengerServiceClient.php index 177dfe4..18fe027 100644 --- a/tests/genproto/Chat/Api/V1/MessengerServiceClient.php +++ b/tests/genproto/Chat/Api/V1/MessengerServiceClient.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/chat_v1.proto */ diff --git a/tests/genproto/Chat/Api/V1/MessengerServiceServer.php b/tests/genproto/Chat/Api/V1/MessengerServiceServer.php index 41cf805..f63e609 100644 --- a/tests/genproto/Chat/Api/V1/MessengerServiceServer.php +++ b/tests/genproto/Chat/Api/V1/MessengerServiceServer.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/chat_v1.proto */ diff --git a/tests/genproto/Chat/Api/V1/MessengerServiceServerRegistry.php b/tests/genproto/Chat/Api/V1/MessengerServiceServerRegistry.php index 3632285..76e6fe3 100644 --- a/tests/genproto/Chat/Api/V1/MessengerServiceServerRegistry.php +++ b/tests/genproto/Chat/Api/V1/MessengerServiceServerRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/chat_v1.proto */ diff --git a/tests/genproto/Chat/Api/V1/TestsProtosChatV1DescriptorRegistry.php b/tests/genproto/Chat/Api/V1/TestsProtosChatV1DescriptorRegistry.php index cae9840..c085d87 100644 --- a/tests/genproto/Chat/Api/V1/TestsProtosChatV1DescriptorRegistry.php +++ b/tests/genproto/Chat/Api/V1/TestsProtosChatV1DescriptorRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/chat_v1.proto */ diff --git a/tests/genproto/Chat/Api/V1/autoload.metadata.php b/tests/genproto/Chat/Api/V1/autoload.metadata.php index 565556d..1972cd2 100644 --- a/tests/genproto/Chat/Api/V1/autoload.metadata.php +++ b/tests/genproto/Chat/Api/V1/autoload.metadata.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 */ diff --git a/tests/genproto/Echos/Api/V1/EchoRequest.php b/tests/genproto/Echos/Api/V1/EchoRequest.php index 50fac9b..784b54b 100644 --- a/tests/genproto/Echos/Api/V1/EchoRequest.php +++ b/tests/genproto/Echos/Api/V1/EchoRequest.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/echo_v1.proto */ diff --git a/tests/genproto/Echos/Api/V1/EchoResponse.php b/tests/genproto/Echos/Api/V1/EchoResponse.php index 712f9df..05773b7 100644 --- a/tests/genproto/Echos/Api/V1/EchoResponse.php +++ b/tests/genproto/Echos/Api/V1/EchoResponse.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/echo_v1.proto */ diff --git a/tests/genproto/Echos/Api/V1/EchoServiceClient.php b/tests/genproto/Echos/Api/V1/EchoServiceClient.php index 6ab257a..1fbb5ff 100644 --- a/tests/genproto/Echos/Api/V1/EchoServiceClient.php +++ b/tests/genproto/Echos/Api/V1/EchoServiceClient.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/echo_v1.proto */ diff --git a/tests/genproto/Echos/Api/V1/EchoServiceServer.php b/tests/genproto/Echos/Api/V1/EchoServiceServer.php index 132ecd6..53ba7c3 100644 --- a/tests/genproto/Echos/Api/V1/EchoServiceServer.php +++ b/tests/genproto/Echos/Api/V1/EchoServiceServer.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/echo_v1.proto */ diff --git a/tests/genproto/Echos/Api/V1/EchoServiceServerRegistry.php b/tests/genproto/Echos/Api/V1/EchoServiceServerRegistry.php index 2166e52..aa3cc35 100644 --- a/tests/genproto/Echos/Api/V1/EchoServiceServerRegistry.php +++ b/tests/genproto/Echos/Api/V1/EchoServiceServerRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/echo_v1.proto */ diff --git a/tests/genproto/Echos/Api/V1/TestsProtosEchoV1DescriptorRegistry.php b/tests/genproto/Echos/Api/V1/TestsProtosEchoV1DescriptorRegistry.php index 23937c6..684402b 100644 --- a/tests/genproto/Echos/Api/V1/TestsProtosEchoV1DescriptorRegistry.php +++ b/tests/genproto/Echos/Api/V1/TestsProtosEchoV1DescriptorRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/echo_v1.proto */ diff --git a/tests/genproto/Echos/Api/V1/autoload.metadata.php b/tests/genproto/Echos/Api/V1/autoload.metadata.php index 042c19c..8dbdfe7 100644 --- a/tests/genproto/Echos/Api/V1/autoload.metadata.php +++ b/tests/genproto/Echos/Api/V1/autoload.metadata.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 */ diff --git a/tests/genproto/File/Api/V1/Chunk.php b/tests/genproto/File/Api/V1/Chunk.php index ad48142..5810e3a 100644 --- a/tests/genproto/File/Api/V1/Chunk.php +++ b/tests/genproto/File/Api/V1/Chunk.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/file_v1.proto */ diff --git a/tests/genproto/File/Api/V1/FileInfo.php b/tests/genproto/File/Api/V1/FileInfo.php index ddeaa38..bbfc773 100644 --- a/tests/genproto/File/Api/V1/FileInfo.php +++ b/tests/genproto/File/Api/V1/FileInfo.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/file_v1.proto */ diff --git a/tests/genproto/File/Api/V1/FileServiceClient.php b/tests/genproto/File/Api/V1/FileServiceClient.php index 66c3543..e0ad74f 100644 --- a/tests/genproto/File/Api/V1/FileServiceClient.php +++ b/tests/genproto/File/Api/V1/FileServiceClient.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/file_v1.proto */ diff --git a/tests/genproto/File/Api/V1/FileServiceServer.php b/tests/genproto/File/Api/V1/FileServiceServer.php index 1fbaaf2..77b3eff 100644 --- a/tests/genproto/File/Api/V1/FileServiceServer.php +++ b/tests/genproto/File/Api/V1/FileServiceServer.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/file_v1.proto */ diff --git a/tests/genproto/File/Api/V1/FileServiceServerRegistry.php b/tests/genproto/File/Api/V1/FileServiceServerRegistry.php index e15a181..c2e2ce8 100644 --- a/tests/genproto/File/Api/V1/FileServiceServerRegistry.php +++ b/tests/genproto/File/Api/V1/FileServiceServerRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/file_v1.proto */ diff --git a/tests/genproto/File/Api/V1/TestsProtosFileV1DescriptorRegistry.php b/tests/genproto/File/Api/V1/TestsProtosFileV1DescriptorRegistry.php index fba3e1b..fe6fe75 100644 --- a/tests/genproto/File/Api/V1/TestsProtosFileV1DescriptorRegistry.php +++ b/tests/genproto/File/Api/V1/TestsProtosFileV1DescriptorRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/file_v1.proto */ diff --git a/tests/genproto/File/Api/V1/autoload.metadata.php b/tests/genproto/File/Api/V1/autoload.metadata.php index d5ffaed..da6e84e 100644 --- a/tests/genproto/File/Api/V1/autoload.metadata.php +++ b/tests/genproto/File/Api/V1/autoload.metadata.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 */ diff --git a/tests/genproto/Topic/Api/V1/Event.php b/tests/genproto/Topic/Api/V1/Event.php index 9f7c100..b77df80 100644 --- a/tests/genproto/Topic/Api/V1/Event.php +++ b/tests/genproto/Topic/Api/V1/Event.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/topic_v1.proto */ diff --git a/tests/genproto/Topic/Api/V1/SubscribeRequest.php b/tests/genproto/Topic/Api/V1/SubscribeRequest.php index 58fa66f..e3fabf6 100644 --- a/tests/genproto/Topic/Api/V1/SubscribeRequest.php +++ b/tests/genproto/Topic/Api/V1/SubscribeRequest.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/topic_v1.proto */ diff --git a/tests/genproto/Topic/Api/V1/TestsProtosTopicV1DescriptorRegistry.php b/tests/genproto/Topic/Api/V1/TestsProtosTopicV1DescriptorRegistry.php index 744320a..a9d884f 100644 --- a/tests/genproto/Topic/Api/V1/TestsProtosTopicV1DescriptorRegistry.php +++ b/tests/genproto/Topic/Api/V1/TestsProtosTopicV1DescriptorRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/topic_v1.proto */ diff --git a/tests/genproto/Topic/Api/V1/TopicServiceClient.php b/tests/genproto/Topic/Api/V1/TopicServiceClient.php index 511c6a1..184dd04 100644 --- a/tests/genproto/Topic/Api/V1/TopicServiceClient.php +++ b/tests/genproto/Topic/Api/V1/TopicServiceClient.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/topic_v1.proto */ diff --git a/tests/genproto/Topic/Api/V1/TopicServiceServer.php b/tests/genproto/Topic/Api/V1/TopicServiceServer.php index 9306112..3e09a3d 100644 --- a/tests/genproto/Topic/Api/V1/TopicServiceServer.php +++ b/tests/genproto/Topic/Api/V1/TopicServiceServer.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/topic_v1.proto */ diff --git a/tests/genproto/Topic/Api/V1/TopicServiceServerRegistry.php b/tests/genproto/Topic/Api/V1/TopicServiceServerRegistry.php index 271f693..2693d39 100644 --- a/tests/genproto/Topic/Api/V1/TopicServiceServerRegistry.php +++ b/tests/genproto/Topic/Api/V1/TopicServiceServerRegistry.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 * Source: tests/protos/topic_v1.proto */ diff --git a/tests/genproto/Topic/Api/V1/autoload.metadata.php b/tests/genproto/Topic/Api/V1/autoload.metadata.php index 0a5a7df..85988a7 100644 --- a/tests/genproto/Topic/Api/V1/autoload.metadata.php +++ b/tests/genproto/Topic/Api/V1/autoload.metadata.php @@ -3,7 +3,7 @@ /** * Code generated by thesis/protoc-plugin. DO NOT EDIT. * Versions: - * thesis/protoc-plugin — v0.1.21 + * thesis/protoc-plugin — v0.1.22 * protoc — v6.33.5 */