Skip to content

Commit dd93a8c

Browse files
committed
added sharded client and client interface
1 parent 7157488 commit dd93a8c

File tree

7 files changed

+284
-5
lines changed

7 files changed

+284
-5
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,19 @@ $client->get("key");
4242
$client->delete("key");
4343
$client->putIf("key", "newValue", "expectedPreviousValue");
4444
$client->deleteIf("key", "expectedPreviousValue");
45-
```
45+
```
46+
47+
#### Sharded client
48+
```php
49+
<?php
50+
51+
$clients = [
52+
new Aternos\Etcd\Client("hostA:2379"),
53+
new Aternos\Etcd\Client("hostB:2379"),
54+
new Aternos\Etcd\Client("hostC:2379")
55+
];
56+
$shardedClient = new Aternos\Etcd\ShardedClient($clients);
57+
58+
$shardedClient->put("key", "value");
59+
$shardedClient->get("key");
60+
```

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"require": {
66
"google/protobuf": "v3.6.1.3",
77
"grpc/grpc": "^1.15",
8-
"ext-grpc": "*"
8+
"ext-grpc": "*",
9+
"flexihash/flexihash": "^2.0"
910
},
1011
"license": "MIT",
1112
"authors": [

composer.lock

Lines changed: 55 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Client.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
*
2929
* @author Matthias Neid
3030
*/
31-
class Client
31+
class Client implements ClientInterface
3232
{
3333
/**
3434
* @var string
@@ -72,6 +72,11 @@ public function __construct($hostname = "localhost:2379", $username = false, $pa
7272
$this->password = $password;
7373
}
7474

75+
public function getHostname(): string
76+
{
77+
return $this->hostname;
78+
}
79+
7580
/**
7681
* Put a value into the key store
7782
*

src/ClientInterface.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Aternos\Etcd;
4+
5+
use Aternos\Etcd\Exception\Status\InvalidResponseStatusCodeException;
6+
7+
/**
8+
* Interface ClientInterface
9+
*
10+
* @package Aternos\Etcd
11+
*/
12+
interface ClientInterface
13+
{
14+
public function getHostname(): string;
15+
16+
/**
17+
* Put a value into the key store
18+
*
19+
* @param string $key
20+
* @param mixed $value
21+
* @param bool $prevKv Get the previous key value in the response
22+
* @param int $lease
23+
* @param bool $ignoreLease Ignore the current lease
24+
* @param bool $ignoreValue Updates the key using its current value
25+
* @return string|null Returns previous value if $prevKv is set to true
26+
* @throws InvalidResponseStatusCodeException
27+
*/
28+
public function put(string $key, $value, bool $prevKv = false, int $lease = 0, bool $ignoreLease = false, bool $ignoreValue = false);
29+
30+
/**
31+
* Get a key value
32+
*
33+
* @param string $key
34+
* @return bool|string
35+
* @throws InvalidResponseStatusCodeException
36+
*/
37+
public function get(string $key);
38+
39+
/**
40+
* Delete a key
41+
*
42+
* @param string $key
43+
* @return bool
44+
* @throws InvalidResponseStatusCodeException
45+
*/
46+
public function delete(string $key);
47+
48+
/**
49+
* Put $value if $key value matches $previousValue otherwise $returnNewValueOnFail
50+
*
51+
* @param string $key
52+
* @param mixed $value The new value to set
53+
* @param mixed $previousValue The previous value to compare against
54+
* @param bool $returnNewValueOnFail
55+
* @return bool|string
56+
* @throws InvalidResponseStatusCodeException
57+
*/
58+
public function putIf(string $key, $value, $previousValue, bool $returnNewValueOnFail = false);
59+
60+
/**
61+
* Delete if $key value matches $previous value otherwise $returnNewValueOnFail
62+
*
63+
* @param string $key
64+
* @param $previousValue
65+
* @param bool $returnNewValueOnFail
66+
* @return bool|string
67+
* @throws InvalidResponseStatusCodeException
68+
*/
69+
public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail = false);
70+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Aternos\Etcd\Exception;
4+
5+
/**
6+
* Class InvalidClientException
7+
*
8+
* @package Aternos\Etcd\Exception
9+
*/
10+
class InvalidClientException extends \Exception
11+
{
12+
13+
}

src/ShardedClient.php

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
namespace Aternos\Etcd;
4+
5+
use Aternos\Etcd\Exception\InvalidClientException;
6+
use Flexihash\Flexihash;
7+
8+
/**
9+
* Class ShardedClient
10+
*
11+
* @package Aternos\Etcd
12+
*/
13+
class ShardedClient implements ClientInterface
14+
{
15+
/**
16+
* @var ClientInterface[]
17+
*/
18+
protected $clients = [];
19+
20+
/**
21+
* @var ClientInterface[]
22+
*/
23+
protected $keyCache = [];
24+
25+
/**
26+
* @var Flexihash
27+
*/
28+
protected $hash = null;
29+
30+
/**
31+
* ShardedClient constructor.
32+
*
33+
* @param ClientInterface[] $clients
34+
* @throws InvalidClientException
35+
*/
36+
public function __construct(array $clients)
37+
{
38+
foreach ($clients as $client) {
39+
if (!$client instanceof ClientInterface) {
40+
throw new InvalidClientException("Invalid client in client list.");
41+
}
42+
43+
$this->clients[$client->getHostname()] = $client;
44+
}
45+
}
46+
47+
/**
48+
* Get the correct client object for that key through consistent hashing
49+
*
50+
* @param string $key
51+
* @return ClientInterface
52+
* @throws \Flexihash\Exception
53+
*/
54+
protected function getClientFromKey(string $key): ClientInterface
55+
{
56+
if (isset($this->keyCache[$key])) {
57+
return $this->keyCache[$key];
58+
}
59+
60+
if ($this->hash === null) {
61+
$this->hash = new Flexihash();
62+
foreach ($this->clients as $client) {
63+
$this->hash->addTarget($client->getHostname());
64+
}
65+
}
66+
67+
$clientHostname = $this->hash->lookup($key);
68+
$this->keyCache[$key] = $this->clients[$clientHostname];
69+
return $this->keyCache[$key];
70+
}
71+
72+
73+
public function getHostname(): string
74+
{
75+
return implode("-", array_keys($this->clients));
76+
}
77+
78+
/**
79+
* @inheritDoc
80+
* @throws \Flexihash\Exception
81+
*/
82+
public function put(string $key, $value, bool $prevKv = false, int $lease = 0, bool $ignoreLease = false, bool $ignoreValue = false)
83+
{
84+
return $this->getClientFromKey($key)->put($key, $value, $prevKv, $lease, $ignoreLease, $ignoreValue);
85+
}
86+
87+
/**
88+
* @inheritDoc
89+
* @throws \Flexihash\Exception
90+
*/
91+
public function get(string $key)
92+
{
93+
return $this->getClientFromKey($key)->get($key);
94+
}
95+
96+
/**
97+
* @inheritDoc
98+
* @throws \Flexihash\Exception
99+
*/
100+
public function delete(string $key)
101+
{
102+
return $this->getClientFromKey($key)->delete($key);
103+
}
104+
105+
/**
106+
* @inheritDoc
107+
* @throws \Flexihash\Exception
108+
*/
109+
public function putIf(string $key, $value, $previousValue, bool $returnNewValueOnFail = false)
110+
{
111+
return $this->getClientFromKey($key)->putIf($key, $value, $previousValue, $returnNewValueOnFail);
112+
}
113+
114+
/**
115+
* @inheritDoc
116+
* @throws \Flexihash\Exception
117+
*/
118+
public function deleteIf(string $key, $previousValue, bool $returnNewValueOnFail = false)
119+
{
120+
return $this->getClientFromKey($key)->deleteIf($key, $previousValue, $returnNewValueOnFail);
121+
}
122+
}

0 commit comments

Comments
 (0)