Skip to content

Commit 39e626f

Browse files
committed
added optional memcached cache for endpoints
1 parent f7f124d commit 39e626f

File tree

3 files changed

+182
-62
lines changed

3 files changed

+182
-62
lines changed

lib/ArangoDBClient/Connection.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,6 @@ public function parseResponse(HttpResponse $response)
622622
if ($leader) {
623623
// have a different leader
624624
$leader = Endpoint::normalize($leader);
625-
626625
$this->_options->addEndpoint($leader);
627626

628627
} else {

lib/ArangoDBClient/ConnectionOptions.php

Lines changed: 182 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ class ConnectionOptions implements \ArrayAccess
3030
*/
3131
private $_values = [];
3232

33-
/**
34-
* The connection endpoint object
35-
*
36-
* @var Endpoint
37-
*/
38-
private $_endpoint;
39-
4033
/**
4134
* The index into the endpoints array that we will connect to (or are currently
4235
* connected to). This index will be increased in case the currently connected
@@ -49,6 +42,13 @@ class ConnectionOptions implements \ArrayAccess
4942
*/
5043
private $_currentEndpointIndex = 0;
5144

45+
/**
46+
* Optional Memcached instance for endpoints caching
47+
*
48+
* @var Memcached
49+
*/
50+
private $_cache = null;
51+
5252
/**
5353
* Endpoint string index constant
5454
*/
@@ -208,18 +208,43 @@ class ConnectionOptions implements \ArrayAccess
208208
* UTF-8 CHeck Flag
209209
*/
210210
const OPTION_CHECK_UTF8_CONFORM = 'CheckUtf8Conform';
211+
212+
/**
213+
* Entry for memcached servers array
214+
*/
215+
const OPTION_MEMCACHED_SERVERS = 'memcachedServers';
216+
217+
/**
218+
* Entry for memcached options array
219+
*/
220+
const OPTION_MEMCACHED_OPTIONS = 'memcachedOptions';
221+
222+
/**
223+
* Entry for memcached endpoints key
224+
*/
225+
const OPTION_MEMCACHED_ENDPOINTS_KEY = 'memcachedEndpointsKey';
226+
227+
/**
228+
* Entry for memcached persistend id
229+
*/
230+
const OPTION_MEMCACHED_PERSISTENT_ID = 'memcachedPersistentId';
231+
232+
/**
233+
* Entry for memcached cache ttl
234+
*/
235+
const OPTION_MEMCACHED_TTL = 'memcachedTtl';
211236

212237
/**
213238
* Set defaults, use options provided by client and validate them
214239
*
215-
*
216240
* @param array $options - initial options
217241
*
218242
* @throws \ArangoDBClient\ClientException
219243
*/
220244
public function __construct(array $options)
221245
{
222246
$this->_values = array_merge(self::getDefaults(), $options);
247+
$this->loadOptionsFromCache();
223248
$this->validate();
224249
}
225250

@@ -294,22 +319,6 @@ public function offsetGet($offset)
294319
return $this->_values[$offset];
295320
}
296321

297-
/**
298-
* Get the endpoint object for the connection
299-
*
300-
* @throws ClientException
301-
* @return Endpoint - endpoint object
302-
*/
303-
public function getEndpoint()
304-
{
305-
if ($this->_endpoint === null) {
306-
// will also validate the endpoint
307-
$this->_endpoint = new Endpoint($this->getCurrentEndpoint());
308-
}
309-
310-
return $this->_endpoint;
311-
}
312-
313322
/**
314323
* Get the current endpoint to use
315324
*
@@ -344,6 +353,10 @@ public function haveMultipleEndpoints()
344353
*/
345354
public function addEndpoint($endpoint)
346355
{
356+
if (!is_string($endpoint) || !Endpoint::isValid($endpoint)) {
357+
throw new ClientException(sprintf("invalid endpoint specification '%s'", $endpoint));
358+
}
359+
347360
// can only add an endpoint here if the list of endpoints is already an array
348361
if (!is_array($this->_values[self::OPTION_ENDPOINT])) {
349362
// make it an array now
@@ -358,26 +371,37 @@ public function addEndpoint($endpoint)
358371
// we have already got this endpoint
359372
$this->_currentEndpointIndex = $found;
360373
}
374+
375+
$this->storeOptionsInCache();
361376
}
362377

363378
/**
364379
* Return the next endpoint from the list of endpoints
380+
* As a side-effect this function switches to a new endpoint
365381
*
366382
* @return string - the next endpoint
367383
*/
368384
public function nextEndpoint()
369385
{
370-
if (!is_array($this->_values[self::OPTION_ENDPOINT])) {
371-
return $this->_values[self::OPTION_ENDPOINT];
386+
$endpoints = $this->_values[self::OPTION_ENDPOINT];
387+
if (!is_array($endpoints)) {
388+
return $endpoints;
372389
}
373390

374-
$numberOfEndpoints = count($this->_values[self::OPTION_ENDPOINT]);
391+
$numberOfEndpoints = count($endpoints);
392+
375393
$this->_currentEndpointIndex++;
376394
if ($this->_currentEndpointIndex >= $numberOfEndpoints) {
377395
$this->_currentEndpointIndex = 0;
378396
}
379397

380-
return $this->_values[self::OPTION_ENDPOINT][$this->_currentEndpointIndex];
398+
$endpoint = $endpoints[$this->_currentEndpointIndex];
399+
400+
if ($numberOfEndpoints > 1) {
401+
$this->storeOptionsInCache();
402+
}
403+
404+
return $endpoint;
381405
}
382406

383407
/**
@@ -388,35 +412,39 @@ public function nextEndpoint()
388412
private static function getDefaults()
389413
{
390414
return [
391-
self::OPTION_ENDPOINT => null,
392-
self::OPTION_HOST => null,
393-
self::OPTION_PORT => DefaultValues::DEFAULT_PORT,
394-
self::OPTION_FAILOVER_TRIES => DefaultValues::DEFAULT_FAILOVER_TRIES,
395-
self::OPTION_TIMEOUT => DefaultValues::DEFAULT_TIMEOUT,
396-
self::OPTION_CREATE => DefaultValues::DEFAULT_CREATE,
397-
self::OPTION_UPDATE_POLICY => DefaultValues::DEFAULT_UPDATE_POLICY,
398-
self::OPTION_REPLACE_POLICY => DefaultValues::DEFAULT_REPLACE_POLICY,
399-
self::OPTION_DELETE_POLICY => DefaultValues::DEFAULT_DELETE_POLICY,
400-
self::OPTION_REVISION => null,
401-
self::OPTION_WAIT_SYNC => DefaultValues::DEFAULT_WAIT_SYNC,
402-
self::OPTION_BATCHSIZE => null,
403-
self::OPTION_JOURNAL_SIZE => DefaultValues::DEFAULT_JOURNAL_SIZE,
404-
self::OPTION_IS_SYSTEM => false,
405-
self::OPTION_IS_VOLATILE => DefaultValues::DEFAULT_IS_VOLATILE,
406-
self::OPTION_CONNECTION => DefaultValues::DEFAULT_CONNECTION,
407-
self::OPTION_TRACE => null,
408-
self::OPTION_ENHANCED_TRACE => false,
409-
self::OPTION_VERIFY_CERT => DefaultValues::DEFAULT_VERIFY_CERT,
410-
self::OPTION_ALLOW_SELF_SIGNED => DefaultValues::DEFAULT_ALLOW_SELF_SIGNED,
411-
self::OPTION_CIPHERS => DefaultValues::DEFAULT_CIPHERS,
412-
self::OPTION_AUTH_USER => null,
413-
self::OPTION_AUTH_PASSWD => null,
414-
self::OPTION_AUTH_TYPE => DefaultValues::DEFAULT_AUTH_TYPE,
415-
self::OPTION_RECONNECT => false,
416-
self::OPTION_BATCH => false,
417-
self::OPTION_BATCHPART => false,
418-
self::OPTION_DATABASE => '_system',
419-
self::OPTION_CHECK_UTF8_CONFORM => DefaultValues::DEFAULT_CHECK_UTF8_CONFORM
415+
self::OPTION_ENDPOINT => null,
416+
self::OPTION_HOST => null,
417+
self::OPTION_PORT => DefaultValues::DEFAULT_PORT,
418+
self::OPTION_FAILOVER_TRIES => DefaultValues::DEFAULT_FAILOVER_TRIES,
419+
self::OPTION_TIMEOUT => DefaultValues::DEFAULT_TIMEOUT,
420+
self::OPTION_MEMCACHED_PERSISTENT_ID => 'arangodb-php-pool',
421+
self::OPTION_MEMCACHED_OPTIONS => [ ],
422+
self::OPTION_MEMCACHED_ENDPOINTS_KEY => 'arangodb-php-endpoints',
423+
self::OPTION_MEMCACHED_TTL => 600,
424+
self::OPTION_CREATE => DefaultValues::DEFAULT_CREATE,
425+
self::OPTION_UPDATE_POLICY => DefaultValues::DEFAULT_UPDATE_POLICY,
426+
self::OPTION_REPLACE_POLICY => DefaultValues::DEFAULT_REPLACE_POLICY,
427+
self::OPTION_DELETE_POLICY => DefaultValues::DEFAULT_DELETE_POLICY,
428+
self::OPTION_REVISION => null,
429+
self::OPTION_WAIT_SYNC => DefaultValues::DEFAULT_WAIT_SYNC,
430+
self::OPTION_BATCHSIZE => null,
431+
self::OPTION_JOURNAL_SIZE => DefaultValues::DEFAULT_JOURNAL_SIZE,
432+
self::OPTION_IS_SYSTEM => false,
433+
self::OPTION_IS_VOLATILE => DefaultValues::DEFAULT_IS_VOLATILE,
434+
self::OPTION_CONNECTION => DefaultValues::DEFAULT_CONNECTION,
435+
self::OPTION_TRACE => null,
436+
self::OPTION_ENHANCED_TRACE => false,
437+
self::OPTION_VERIFY_CERT => DefaultValues::DEFAULT_VERIFY_CERT,
438+
self::OPTION_ALLOW_SELF_SIGNED => DefaultValues::DEFAULT_ALLOW_SELF_SIGNED,
439+
self::OPTION_CIPHERS => DefaultValues::DEFAULT_CIPHERS,
440+
self::OPTION_AUTH_USER => null,
441+
self::OPTION_AUTH_PASSWD => null,
442+
self::OPTION_AUTH_TYPE => DefaultValues::DEFAULT_AUTH_TYPE,
443+
self::OPTION_RECONNECT => false,
444+
self::OPTION_BATCH => false,
445+
self::OPTION_BATCHPART => false,
446+
self::OPTION_DATABASE => '_system',
447+
self::OPTION_CHECK_UTF8_CONFORM => DefaultValues::DEFAULT_CHECK_UTF8_CONFORM
420448
];
421449
}
422450

@@ -467,10 +495,12 @@ private function validate()
467495
unset($this->_values[self::OPTION_HOST]);
468496
}
469497

470-
// set up a new endpoint, this will also validate it
471-
$this->getEndpoint();
472-
498+
// validate endpoint
473499
$ep = $this->getCurrentEndpoint();
500+
if (!Endpoint::isValid($ep)) {
501+
throw new ClientException(sprintf("invalid endpoint specification '%s'", $ep));
502+
}
503+
474504
$type = Endpoint::getType($ep);
475505
if ($type === Endpoint::TYPE_UNIX) {
476506
// must set port to 0 for UNIX domain sockets
@@ -516,6 +546,98 @@ private function validate()
516546
UpdatePolicy::validate($this->_values[self::OPTION_REPLACE_POLICY]);
517547
UpdatePolicy::validate($this->_values[self::OPTION_DELETE_POLICY]);
518548
}
549+
550+
551+
/**
552+
* load and merge connection options from optional Memcached cache into
553+
* ihe current settings
554+
*
555+
* @return void
556+
*/
557+
private function loadOptionsFromCache()
558+
{
559+
$cache = $this->getEndpointsCache();
560+
561+
if ($cache === null) {
562+
return;
563+
}
564+
565+
$endpoints = $cache->get($this->_values[self::OPTION_MEMCACHED_ENDPOINTS_KEY]);
566+
if ($endpoints) {
567+
$this->_values[self::OPTION_ENDPOINT] = $endpoints;
568+
}
569+
}
570+
571+
/**
572+
* store the updated options in the optional Memcached cache
573+
*
574+
* @return void
575+
*/
576+
private function storeOptionsInCache()
577+
{
578+
$endpoints = $this->_values[self::OPTION_ENDPOINT];
579+
$numberOfEndpoints = count($endpoints);
580+
581+
if ($numberOfEndpoints <= 1) {
582+
return;
583+
}
584+
585+
// now try to store the updated values in the cache
586+
$cache = $this->getEndpointsCache();
587+
if ($cache === null) {
588+
return;
589+
}
590+
591+
$update = [ $endpoints[$this->_currentEndpointIndex] ];
592+
for ($i = 0; $i < $numberOfEndpoints; ++$i) {
593+
if ($i !== $this->_currentEndpointIndex) {
594+
$update[] = $endpoints[$i];
595+
}
596+
}
597+
598+
$ttl = (int) $this->_values[self::OPTION_MEMCACHED_TTL];
599+
$cache->set($this->_values[self::OPTION_MEMCACHED_ENDPOINTS_KEY], $update, $ttl);
600+
}
601+
602+
/**
603+
* Initialize and return a memcached cache instance,
604+
* if option "memcachedServers" is set
605+
*
606+
* @return Memcached - memcached server instance if configured or null if not
607+
*/
608+
private function getEndpointsCache()
609+
{
610+
if ($this->_cache === null) {
611+
if (!isset($this->_values[self::OPTION_MEMCACHED_SERVERS])) {
612+
return null;
613+
}
614+
if (!class_exists('Memcached', false)) {
615+
return null;
616+
}
617+
618+
$servers = $this->_values[self::OPTION_MEMCACHED_SERVERS];
619+
if (!is_array($servers)) {
620+
throw new ClientException('Invalid memcached servers list. should be an array of servers');
621+
}
622+
623+
$cache = new \Memcached(self::OPTION_MEMCACHED_PERSISTENT_ID);
624+
if (empty($cache->getServerList())) {
625+
$cache->addServers($servers);
626+
}
627+
628+
if (isset($this->_values[self::OPTION_MEMCACHED_OPTIONS])) {
629+
$options = $this->_values[self::OPTION_MEMCACHED_OPTIONS];
630+
if (!is_array($options)) {
631+
throw new ClientException('Invalid memcached options list. should be an array of options');
632+
}
633+
$cache->setOptions($options);
634+
}
635+
636+
$this->_cache = $cache;
637+
638+
}
639+
return $this->_cache;
640+
}
519641
}
520642

521643
class_alias(ConnectionOptions::class, '\triagens\ArangoDb\ConnectionOptions');

lib/ArangoDBClient/HttpHelper.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ class HttpHelper
8282
public static function createConnection(ConnectionOptions $options)
8383
{
8484
$endpoint = $options->getCurrentEndpoint();
85-
8685
$context = stream_context_create();
8786

8887
if (Endpoint::getType($endpoint) === Endpoint::TYPE_SSL) {

0 commit comments

Comments
 (0)