From 0cb716e5f8a9c7d0f6bb29ac332363bbee8fc6fc Mon Sep 17 00:00:00 2001 From: Chris Price Date: Tue, 7 Nov 2023 10:07:31 -0800 Subject: [PATCH] feat: add `close` method for explicitly closing client (#174) This is intended to help us determine whether or not a persistent gRPC connection that gets into a bad state can be explicitly closed and re-opened to restore the environment to a healthy state. --- src/Cache/CacheClient.php | 13 +++++++++++++ src/Cache/Internal/ControlGrpcManager.php | 11 ++++++++--- src/Cache/Internal/IdleDataClientWrapper.php | 4 ++++ src/Cache/Internal/ScsControlClient.php | 4 ++++ tests/Cache/CacheClientTest.php | 11 +++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/Cache/CacheClient.php b/src/Cache/CacheClient.php index db111b0f..62f0ef97 100644 --- a/src/Cache/CacheClient.php +++ b/src/Cache/CacheClient.php @@ -96,6 +96,19 @@ public function __construct( } } + /** + * Close the client and free up all associated resources. NOTE: the client object will not be usable after calling + * this method. + */ + public function close(): void + { + $this->controlClient->close(); + foreach ($this->dataClients as $dataClient) { + $dataClient->close(); + } + } + + /** * Assigns a LoggerInterface logging object to the client. * diff --git a/src/Cache/Internal/ControlGrpcManager.php b/src/Cache/Internal/ControlGrpcManager.php index 2a8b3ec7..019a5b09 100644 --- a/src/Cache/Internal/ControlGrpcManager.php +++ b/src/Cache/Internal/ControlGrpcManager.php @@ -16,6 +16,7 @@ class ControlGrpcManager { public ScsControlClient $client; + private Channel $channel; public function __construct(ICredentialProvider $authProvider) { @@ -24,14 +25,18 @@ public function __construct(ICredentialProvider $authProvider) if ($authProvider->getTrustedControlEndpointCertificateName()) { $channelArgs["grpc.ssl_target_name_override"] = $authProvider->getTrustedControlEndpointCertificateName(); } - $channel = new Channel($endpoint, $channelArgs); + $this->channel = new Channel($endpoint, $channelArgs); $interceptors = [ new AuthorizationInterceptor($authProvider->getAuthToken()), new AgentInterceptor(), ]; - $channel = Interceptor::intercept($channel, $interceptors); + $interceptedChannel = Interceptor::intercept($this->channel, $interceptors); $options = []; - $this->client = new ScsControlClient($endpoint, $options, $channel); + $this->client = new ScsControlClient($endpoint, $options, $interceptedChannel); + } + + public function close(): void { + $this->channel->close(); } } diff --git a/src/Cache/Internal/IdleDataClientWrapper.php b/src/Cache/Internal/IdleDataClientWrapper.php index 929744b0..71ffc61c 100644 --- a/src/Cache/Internal/IdleDataClientWrapper.php +++ b/src/Cache/Internal/IdleDataClientWrapper.php @@ -42,6 +42,10 @@ public function getClient(): ScsDataClient { return $this->client; } + public function close(): void { + $this->client->close(); + } + private function getMilliseconds(): int { return (int)(gettimeofday(true) * 1000); } diff --git a/src/Cache/Internal/ScsControlClient.php b/src/Cache/Internal/ScsControlClient.php index 9fe8f2c2..f6b9178a 100644 --- a/src/Cache/Internal/ScsControlClient.php +++ b/src/Cache/Internal/ScsControlClient.php @@ -45,6 +45,10 @@ public function __construct(ILoggerFactory $loggerFactory, ICredentialProvider $ $this->setLogger($this->loggerFactory->getLogger(get_class($this))); } + public function close(): void { + $this->grpcManager->close(); + } + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; diff --git a/tests/Cache/CacheClientTest.php b/tests/Cache/CacheClientTest.php index ca004eb3..4ab9758a 100644 --- a/tests/Cache/CacheClientTest.php +++ b/tests/Cache/CacheClientTest.php @@ -68,6 +68,17 @@ private function getConfigurationWithDeadline(int $deadline) return new Configuration($loggerFactory, $transportStrategy); } + public function testCreateAndCloseClient() { + $client = new CacheClient($this->configuration, $this->authProvider, $this->DEFAULT_TTL_SECONDS); + $response = $client->listCaches(); + $this->assertNull($response->asError()); + $client->close(); + $client = new CacheClient($this->configuration, $this->authProvider, $this->DEFAULT_TTL_SECONDS); + $response = $client->listCaches(); + $this->assertNull($response->asError()); + $client->close(); + } + // Happy path test public function testCreateSetGetDelete()