Merge "User: Avoid deprecated Linker::link()"
[lhc/web/wiklou.git] / includes / config / EtcdConfig.php
index d3fbd65..c57eba7 100644 (file)
@@ -1,7 +1,5 @@
 <?php
 /**
- * Copyright 2017
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,7 +16,6 @@
  * http://www.gnu.org/copyleft/gpl.html
  *
  * @file
- * @author Aaron Schulz
  */
 
 use Psr\Log\LoggerAwareInterface;
@@ -54,8 +51,6 @@ class EtcdConfig implements Config, LoggerAwareInterface {
        private $skewCacheTTL;
        /** @var integer */
        private $timeout;
-       /** @var string */
-       private $directoryHash;
 
        /**
         * @param array $params Parameter map:
@@ -75,20 +70,19 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                        'encoding' => 'JSON',
                        'cacheTTL' => 10,
                        'skewTTL' => 1,
-                       'timeout' => 10
+                       'timeout' => 2
                ];
 
                $this->host = $params['host'];
                $this->protocol = $params['protocol'];
                $this->directory = trim( $params['directory'], '/' );
-               $this->directoryHash = sha1( $this->directory );
                $this->encoding = $params['encoding'];
                $this->skewCacheTTL = $params['skewTTL'];
                $this->baseCacheTTL = max( $params['cacheTTL'] - $this->skewCacheTTL, 0 );
                $this->timeout = $params['timeout'];
 
                if ( !isset( $params['cache'] ) ) {
-                       $this->srvCache = new HashBagOStuff( [] );
+                       $this->srvCache = new HashBagOStuff();
                } elseif ( $params['cache'] instanceof BagOStuff ) {
                        $this->srvCache = $params['cache'];
                } else {
@@ -98,12 +92,14 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                $this->logger = new Psr\Log\NullLogger();
                $this->http = new MultiHttpClient( [
                        'connTimeout' => $this->timeout,
-                       'reqTimeout' => $this->timeout
+                       'reqTimeout' => $this->timeout,
+                       'logger' => $this->logger
                ] );
        }
 
        public function setLogger( LoggerInterface $logger ) {
                $this->logger = $logger;
+               $this->http->setLogger( $logger );
        }
 
        public function has( $name ) {
@@ -122,13 +118,20 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                return $this->procCache['config'][$name];
        }
 
+       /**
+        * @throws ConfigException
+        */
        private function load() {
                if ( $this->procCache !== null ) {
                        return; // already loaded
                }
 
                $now = microtime( true );
-               $key = $this->srvCache->makeKey( 'variable', $this->directoryHash );
+               $key = $this->srvCache->makeGlobalKey(
+                       __CLASS__,
+                       $this->host,
+                       $this->directory
+               );
 
                // Get the cached value or block until it is regenerated (by this or another thread)...
                $data = null; // latest config info
@@ -148,24 +151,24 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                                if ( $this->srvCache->lock( $key, 0, $this->baseCacheTTL ) ) {
                                        try {
                                                list( $config, $error, $retry ) = $this->fetchAllFromEtcd();
-                                               if ( $config === null ) {
-                                                       $this->logger->error( "Failed to fetch configuration: $error" );
-                                                       // Fail fast if the error is likely to just keep happening
-                                                       return $retry
-                                                               ? WaitConditionLoop::CONDITION_CONTINUE
-                                                               : WaitConditionLoop::CONDITION_FAILED;
-                                               }
-
-                                               // Avoid having all servers expire cache keys at the same time
-                                               $expiry = microtime( true ) + $this->baseCacheTTL;
-                                               $expiry += mt_rand( 0, 1e6 ) / 1e6 * $this->skewCacheTTL;
+                                               if ( is_array( $config ) ) {
+                                                       // Avoid having all servers expire cache keys at the same time
+                                                       $expiry = microtime( true ) + $this->baseCacheTTL;
+                                                       $expiry += mt_rand( 0, 1e6 ) / 1e6 * $this->skewCacheTTL;
 
-                                               $data = [ 'config' => $config, 'expires' => $expiry ];
-                                               $this->srvCache->set( $key, $data, BagOStuff::TTL_INDEFINITE );
+                                                       $data = [ 'config' => $config, 'expires' => $expiry ];
+                                                       $this->srvCache->set( $key, $data, BagOStuff::TTL_INDEFINITE );
 
-                                               $this->logger->info( "Refreshed stale etcd configuration cache." );
+                                                       $this->logger->info( "Refreshed stale etcd configuration cache." );
 
-                                               return WaitConditionLoop::CONDITION_REACHED;
+                                                       return WaitConditionLoop::CONDITION_REACHED;
+                                               } else {
+                                                       $this->logger->error( "Failed to fetch configuration: $error" );
+                                                       if ( !$retry ) {
+                                                               // Fail fast since the error is likely to keep happening
+                                                               return WaitConditionLoop::CONDITION_FAILED;
+                                                       }
+                                               }
                                        } finally {
                                                $this->srvCache->unlock( $key ); // release mutex
                                        }
@@ -211,7 +214,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
                        }
 
                        // Avoid the server next time if that failed
-                       $dsd->removeServer( $server, $servers );
+                       $servers = $dsd->removeServer( $server, $servers );
                } while ( $servers );
 
                return [ $config, $error, $retry ];
@@ -251,7 +254,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
 
                        $name = basename( $node['key'] );
                        $value = $this->unserialize( $node['value'] );
-                       if ( !is_array( $value ) || !isset( $value['val'] ) ) {
+                       if ( !is_array( $value ) || !array_key_exists( 'val', $value ) ) {
                                return [ null, "Failed to parse value for '$name'.", false ];
                        }