Skip to content

Commit

Permalink
added sharded client and client interface
Browse files Browse the repository at this point in the history
  • Loading branch information
matthi4s committed Mar 21, 2020
1 parent 7157488 commit dd93a8c
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 5 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,19 @@ $client->get("key");
$client->delete("key");
$client->putIf("key", "newValue", "expectedPreviousValue");
$client->deleteIf("key", "expectedPreviousValue");
```
```

#### Sharded client
```php
<?php

$clients = [
new Aternos\Etcd\Client("hostA:2379"),
new Aternos\Etcd\Client("hostB:2379"),
new Aternos\Etcd\Client("hostC:2379")
];
$shardedClient = new Aternos\Etcd\ShardedClient($clients);

$shardedClient->put("key", "value");
$shardedClient->get("key");
```
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"require": {
"google/protobuf": "v3.6.1.3",
"grpc/grpc": "^1.15",
"ext-grpc": "*"
"ext-grpc": "*",
"flexihash/flexihash": "^2.0"
},
"license": "MIT",
"authors": [
Expand Down
57 changes: 55 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
*
* @author Matthias Neid
*/
class Client
class Client implements ClientInterface
{
/**
* @var string
Expand Down Expand Up @@ -72,6 +72,11 @@ public function __construct($hostname = "localhost:2379", $username = false, $pa
$this->password = $password;
}

public function getHostname(): string
{
return $this->hostname;
}

/**
* Put a value into the key store
*
Expand Down
70 changes: 70 additions & 0 deletions src/ClientInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Aternos\Etcd;

use Aternos\Etcd\Exception\Status\InvalidResponseStatusCodeException;

/**
* Interface ClientInterface
*
* @package Aternos\Etcd
*/
interface ClientInterface
{
public function getHostname(): string;

/**
* Put a value into the key store
*
* @param string $key
* @param mixed $value
* @param bool $prevKv Get the previous key value in the response
* @param int $lease
* @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);

/**
* Get a key value
*
* @param string $key
* @return bool|string
* @throws InvalidResponseStatusCodeException
*/
public function get(string $key);

/**
* Delete a key
*
* @param string $key
* @return bool
* @throws InvalidResponseStatusCodeException
*/
public function delete(string $key);

/**
* Put $value if $key value matches $previousValue otherwise $returnNewValueOnFail
*
* @param string $key
* @param mixed $value The new value to set
* @param mixed $previousValue The previous value to compare against
* @param bool $returnNewValueOnFail
* @return bool|string
* @throws InvalidResponseStatusCodeException
*/
public function putIf(string $key, $value, $previousValue, bool $returnNewValueOnFail = false);

/**
* Delete if $key value matches $previous value otherwise $returnNewValueOnFail
*
* @param string $key
* @param $previousValue
* @param bool $returnNewValueOnFail
* @return bool|string
* @throws InvalidResponseStatusCodeException
*/
public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail = false);
}
13 changes: 13 additions & 0 deletions src/Exception/InvalidClientException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Aternos\Etcd\Exception;

/**
* Class InvalidClientException
*
* @package Aternos\Etcd\Exception
*/
class InvalidClientException extends \Exception
{

}
122 changes: 122 additions & 0 deletions src/ShardedClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

namespace Aternos\Etcd;

use Aternos\Etcd\Exception\InvalidClientException;
use Flexihash\Flexihash;

/**
* Class ShardedClient
*
* @package Aternos\Etcd
*/
class ShardedClient implements ClientInterface
{
/**
* @var ClientInterface[]
*/
protected $clients = [];

/**
* @var ClientInterface[]
*/
protected $keyCache = [];

/**
* @var Flexihash
*/
protected $hash = null;

/**
* ShardedClient constructor.
*
* @param ClientInterface[] $clients
* @throws InvalidClientException
*/
public function __construct(array $clients)
{
foreach ($clients as $client) {
if (!$client instanceof ClientInterface) {
throw new InvalidClientException("Invalid client in client list.");
}

$this->clients[$client->getHostname()] = $client;
}
}

/**
* Get the correct client object for that key through consistent hashing
*
* @param string $key
* @return ClientInterface
* @throws \Flexihash\Exception
*/
protected function getClientFromKey(string $key): ClientInterface
{
if (isset($this->keyCache[$key])) {
return $this->keyCache[$key];
}

if ($this->hash === null) {
$this->hash = new Flexihash();
foreach ($this->clients as $client) {
$this->hash->addTarget($client->getHostname());
}
}

$clientHostname = $this->hash->lookup($key);
$this->keyCache[$key] = $this->clients[$clientHostname];
return $this->keyCache[$key];
}


public function getHostname(): string
{
return implode("-", array_keys($this->clients));
}

/**
* @inheritDoc
* @throws \Flexihash\Exception
*/
public function put(string $key, $value, bool $prevKv = false, int $lease = 0, bool $ignoreLease = false, bool $ignoreValue = false)
{
return $this->getClientFromKey($key)->put($key, $value, $prevKv, $lease, $ignoreLease, $ignoreValue);
}

/**
* @inheritDoc
* @throws \Flexihash\Exception
*/
public function get(string $key)
{
return $this->getClientFromKey($key)->get($key);
}

/**
* @inheritDoc
* @throws \Flexihash\Exception
*/
public function delete(string $key)
{
return $this->getClientFromKey($key)->delete($key);
}

/**
* @inheritDoc
* @throws \Flexihash\Exception
*/
public function putIf(string $key, $value, $previousValue, bool $returnNewValueOnFail = false)
{
return $this->getClientFromKey($key)->putIf($key, $value, $previousValue, $returnNewValueOnFail);
}

/**
* @inheritDoc
* @throws \Flexihash\Exception
*/
public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail = false)
{
return $this->getClientFromKey($key)->deleteIf($key, $previousValue, $returnNewValueOnFail);
}
}

0 comments on commit dd93a8c

Please sign in to comment.