diff --git a/src/Client.php b/src/Client.php index aa8b68a..6c7a2b0 100644 --- a/src/Client.php +++ b/src/Client.php @@ -13,6 +13,12 @@ use Etcdserverpb\DeleteRangeRequest; use Etcdserverpb\DeleteRangeResponse; use Etcdserverpb\KVClient; +use Etcdserverpb\LeaseClient; +use Etcdserverpb\LeaseGrantRequest; +use Etcdserverpb\LeaseGrantResponse; +use Etcdserverpb\LeaseKeepAliveRequest; +use Etcdserverpb\LeaseKeepAliveResponse; +use Etcdserverpb\LeaseRevokeRequest; use Etcdserverpb\PutRequest; use Etcdserverpb\PutResponse; use Etcdserverpb\RangeRequest; @@ -21,6 +27,7 @@ use Etcdserverpb\ResponseOp; use Etcdserverpb\TxnRequest; use Etcdserverpb\TxnResponse; +use Exception; use Grpc\ChannelCredentials; /** @@ -63,6 +70,11 @@ class Client implements ClientInterface */ protected $authClient; + /** + * @var LeaseClient + */ + protected $leaseClient; + /** * Client constructor. * @@ -94,14 +106,14 @@ public function getHostname(?string $key = null): string * @param string $key * @param mixed $value * @param bool $prevKv Get the previous key value in the response - * @param int $lease + * @param int $leaseID * @param bool $ignoreLease Ignore the current lease * @param bool $ignoreValue Updates the key using its current value * * @return string|null Returns previous value if $prevKv is set to true * @throws InvalidResponseStatusCodeException */ - public function put(string $key, $value, bool $prevKv = false, int $lease = 0, bool $ignoreLease = false, bool $ignoreValue = false) + public function put(string $key, $value, bool $prevKv = false, int $leaseID = 0, bool $ignoreLease = false, bool $ignoreValue = false) { $client = $this->getKvClient(); @@ -112,7 +124,7 @@ public function put(string $key, $value, bool $prevKv = false, int $lease = 0, b $request->setPrevKv($prevKv); $request->setIgnoreLease($ignoreLease); $request->setIgnoreValue($ignoreValue); - $request->setLease($lease); + $request->setLease($leaseID); /** @var PutResponse $response */ list($response, $status) = $client->Put($request, $this->getMetaData(), $this->getOptions())->wait(); @@ -217,6 +229,68 @@ public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail return $this->requestIf($key, $previousValue, $operation, $returnNewValueOnFail); } + /** + * Get leaseID which can be used with etcd's put + * + * @param int $ttl time-to-live in seconds + * @return int + * @throws InvalidResponseStatusCodeException + */ + public function getLeaseID(int $ttl) + { + $lease = $this->getLeaseClient(); + $leaseRequest = new LeaseGrantRequest(); + $leaseRequest->setTTL($ttl); + + /** @var LeaseGrantResponse $response */ + list($response, $status) = $lease->LeaseGrant($leaseRequest, $this->getMetaData())->wait(); + $this->validateStatus($status); + + return (int)$response->getID(); + } + + /** + * Revoke existing leaseID + * + * @param int $leaseID + * @throws InvalidResponseStatusCodeException + */ + public function revokeLeaseID(int $leaseID) + { + $lease = $this->getLeaseClient(); + $leaseRequest = new LeaseRevokeRequest(); + $leaseRequest->setID($leaseID); + + list(, $status) = $lease->LeaseRevoke($leaseRequest, $this->getMetaData())->wait(); + $this->validateStatus($status); + } + + /** + * Refresh chosen leaseID + * + * @param int $leaseID + * @return int lease TTL + * @throws InvalidResponseStatusCodeException + * @throws Exception + */ + public function refreshLease(int $leaseID) + { + $lease = $this->getLeaseClient(); + $leaseBidi = $lease->LeaseKeepAlive($this->getMetaData()); + $leaseKeepAlive = new LeaseKeepAliveRequest(); + $leaseKeepAlive->setID($leaseID); + /** @noinspection PhpParamsInspection */ + $leaseBidi->write($leaseKeepAlive); + $leaseBidi->writesDone(); + /** @var LeaseKeepAliveResponse $response */ + $response = $leaseBidi->read(); + $leaseBidi->cancel(); + if(empty($response->getID()) || (int)$response->getID() !== $leaseID) + throw new Exception('Could not refresh lease ID: ' . $leaseID); + + return (int)$response->getTTL(); + } + /** * Execute $requestOperation if $key value matches $previous otherwise $returnNewValueOnFail * @@ -279,6 +353,22 @@ protected function requestIf(string $key, $previousValue, RequestOp $requestOper } } + /** + * Get an instance of LeaseClient + * + * @return LeaseClient + */ + protected function getLeaseClient(): LeaseClient + { + if (!$this->leaseClient) { + $this->leaseClient = new LeaseClient($this->hostname, [ + 'credentials' => ChannelCredentials::createInsecure() + ]); + } + + return $this->leaseClient; + } + /** * Get an instance of KVClient * diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 4ab5340..691e3b3 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -3,6 +3,7 @@ namespace Aternos\Etcd; use Aternos\Etcd\Exception\Status\InvalidResponseStatusCodeException; +use Exception; /** * Interface ClientInterface @@ -23,13 +24,13 @@ public function getHostname(?string $key = null): string; * @param string $key * @param mixed $value * @param bool $prevKv Get the previous key value in the response - * @param int $lease + * @param int $leaseID * @param bool $ignoreLease Ignore the current lease * @param bool $ignoreValue Updates the key using its current value * @return string|null Returns previous value if $prevKv is set to true * @throws InvalidResponseStatusCodeException */ - public function put(string $key, $value, bool $prevKv = false, int $lease = 0, bool $ignoreLease = false, bool $ignoreValue = false); + public function put(string $key, $value, bool $prevKv = false, int $leaseID = 0, bool $ignoreLease = false, bool $ignoreValue = false); /** * Get a key value @@ -71,4 +72,31 @@ public function putIf(string $key, $value, $previousValue, bool $returnNewValueO * @throws InvalidResponseStatusCodeException */ public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail = false); + + /** + * Get leaseID which can be used with etcd's put + * + * @param int $ttl time-to-live in seconds + * @return int + * @throws InvalidResponseStatusCodeException + */ + public function getLeaseID(int $ttl); + + /** + * Revoke existing leaseID + * + * @param int $leaseID + * @throws InvalidResponseStatusCodeException + */ + public function revokeLeaseID(int $leaseID); + + /** + * Refresh chosen leaseID + * + * @param int $leaseID + * @return int lease TTL + * @throws InvalidResponseStatusCodeException + * @throws Exception + */ + public function refreshLease(int $leaseID); } \ No newline at end of file diff --git a/src/ShardedClient.php b/src/ShardedClient.php index 6f6096c..0dd7018 100644 --- a/src/ShardedClient.php +++ b/src/ShardedClient.php @@ -3,6 +3,7 @@ namespace Aternos\Etcd; use Aternos\Etcd\Exception\InvalidClientException; +use Flexihash\Exception; use Flexihash\Flexihash; /** @@ -49,7 +50,7 @@ public function __construct(array $clients) * * @param string $key * @return ClientInterface - * @throws \Flexihash\Exception + * @throws Exception */ protected function getClientFromKey(string $key): ClientInterface { @@ -69,9 +70,20 @@ protected function getClientFromKey(string $key): ClientInterface return $this->keyCache[$key]; } + /** + * Get random client + * + * @return ClientInterface + */ + protected function getRandomClient(): ClientInterface + { + $rndIndex = array_rand($this->clients); + return $this->clients[$rndIndex]; + } + /** * @inheritDoc - * @throws \Flexihash\Exception + * @throws Exception */ public function getHostname(?string $key = null): string { @@ -83,7 +95,7 @@ public function getHostname(?string $key = null): string /** * @inheritDoc - * @throws \Flexihash\Exception + * @throws Exception */ public function put(string $key, $value, bool $prevKv = false, int $lease = 0, bool $ignoreLease = false, bool $ignoreValue = false) { @@ -92,7 +104,7 @@ public function put(string $key, $value, bool $prevKv = false, int $lease = 0, b /** * @inheritDoc - * @throws \Flexihash\Exception + * @throws Exception */ public function get(string $key) { @@ -101,7 +113,7 @@ public function get(string $key) /** * @inheritDoc - * @throws \Flexihash\Exception + * @throws Exception */ public function delete(string $key) { @@ -110,7 +122,7 @@ public function delete(string $key) /** * @inheritDoc - * @throws \Flexihash\Exception + * @throws Exception */ public function putIf(string $key, $value, $previousValue, bool $returnNewValueOnFail = false) { @@ -119,10 +131,34 @@ public function putIf(string $key, $value, $previousValue, bool $returnNewValueO /** * @inheritDoc - * @throws \Flexihash\Exception + * @throws Exception */ public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail = false) { return $this->getClientFromKey($key)->deleteIf($key, $previousValue, $returnNewValueOnFail); } + + /** + * @inheritDoc + */ + public function getLeaseID(int $ttl) + { + return $this->getRandomClient()->getLeaseID($ttl); + } + + /** + * @inheritDoc + */ + public function revokeLeaseID(int $leaseID) + { + return $this->getRandomClient()->revokeLeaseID($leaseID); + } + + /** + * @inheritDoc + */ + public function refreshLease(int $leaseID) + { + return $this->getRandomClient()->refreshLease($leaseID); + } } \ No newline at end of file