From 1aa82e144f61aef1ef1754a26eb8f59cad358cd1 Mon Sep 17 00:00:00 2001 From: gloas <gloas@afi-sa.fr> Date: Thu, 21 Oct 2021 10:48:32 +0200 Subject: [PATCH] hotline #142649 add memcached status --- VERSIONS_HOTLINE/142649 | 3 +- .../admin/controllers/SystemeController.php | 14 +- .../scripts/systeme/memcached-status.phtml | 2 + .../ZendAfi/View/Helper/Admin/ContentNav.php | 1 + .../View/Helper/Admin/MemcachedStatus.php | 205 ++++++++++++++++++ library/startup.php | 15 +- public/admin/skins/bokeh72/config.json | 1 + public/admin/skins/bokeh74/config.json | 2 +- public/admin/skins/noel/config.json | 1 + public/admin/skins/retro/config.json | 1 + .../controllers/AdminIndexControllerTest.php | 6 + .../controllers/SystemeControllerTest.php | 150 +++++++++++++ tests/library/Class/CatalogueTest.php | 2 +- .../ZendAfi/View/Helper/Accueil/SitoTest.php | 2 +- 14 files changed, 397 insertions(+), 8 deletions(-) create mode 100644 application/modules/admin/views/scripts/systeme/memcached-status.phtml create mode 100644 library/ZendAfi/View/Helper/Admin/MemcachedStatus.php diff --git a/VERSIONS_HOTLINE/142649 b/VERSIONS_HOTLINE/142649 index 444d82c9316..93beac64fa3 100644 --- a/VERSIONS_HOTLINE/142649 +++ b/VERSIONS_HOTLINE/142649 @@ -1,2 +1,3 @@ - ticket #142649 : Magasin de thèmes : Amélioration des performances du rendu liste à interactions. - Amélioration des performances de rendu des exemplaires de périodiques. Lorsqu'une notice de périodiques a plus de 20 exemplaires, elle utilise le rendu de liste à interactions. \ No newline at end of file + Amélioration des performances de rendu des exemplaires de périodiques. Lorsqu'une notice de périodiques a plus de 20 exemplaires, elle utilise le rendu de liste à interactions. + Administration : Ajout d'un écran d'état de l'utilisation de Memcached. diff --git a/application/modules/admin/controllers/SystemeController.php b/application/modules/admin/controllers/SystemeController.php index 5db2c4b5c87..67a63dc7bd8 100644 --- a/application/modules/admin/controllers/SystemeController.php +++ b/application/modules/admin/controllers/SystemeController.php @@ -19,8 +19,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class Admin_SystemeController extends Zend_Controller_Action { - use Trait_Translator, Trait_StaticFileSystem; +class Admin_SystemeController extends ZendAfi_Controller_Action { + + use + Trait_StaticFileSystem, + Trait_TimeSource; + public function webservicesAction() { $this->view->titre = $this->_('Test des Web Services'); @@ -285,5 +289,9 @@ class Admin_SystemeController extends Zend_Controller_Action { public function statusAction() { $this->view->titre = $this->_('Etat du système'); } + + + public function memcachedStatusAction() { + $this->view->titre = $this->_('État de la configuration et de l\'utilisation de Memcached à %s', static::getCurrentDateTime()); + } } -?> diff --git a/application/modules/admin/views/scripts/systeme/memcached-status.phtml b/application/modules/admin/views/scripts/systeme/memcached-status.phtml new file mode 100644 index 00000000000..8d8b6e5a878 --- /dev/null +++ b/application/modules/admin/views/scripts/systeme/memcached-status.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->Admin_MemcachedStatus(); diff --git a/library/ZendAfi/View/Helper/Admin/ContentNav.php b/library/ZendAfi/View/Helper/Admin/ContentNav.php index 6a978af8b96..c9f1ba92415 100644 --- a/library/ZendAfi/View/Helper/Admin/ContentNav.php +++ b/library/ZendAfi/View/Helper/Admin/ContentNav.php @@ -158,6 +158,7 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe ['sendmail_tests', $this->_('Test envoi mails'), '/admin/systeme/mailtest', [], $is_super_admin], ['php', $this->_('Informations système'), '/admin/systeme/phpinfo', [], $is_super_admin], + ['memcached', $this->_('Informations Memcached'), '/admin/systeme/memcached-status', [], $is_super_admin], ['image_cache', $this->_('Cache des images'), '/admin/systeme/cacheimages', [], $is_admin], ['migrate_comments', $this->_('Import avis opac2'), '/admin/systeme/importavisopac2', diff --git a/library/ZendAfi/View/Helper/Admin/MemcachedStatus.php b/library/ZendAfi/View/Helper/Admin/MemcachedStatus.php new file mode 100644 index 00000000000..aa3adbc4503 --- /dev/null +++ b/library/ZendAfi/View/Helper/Admin/MemcachedStatus.php @@ -0,0 +1,205 @@ +<?php +/** + * Copyright (c) 2012-2021, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_Admin_MemcachedStatus extends ZendAfi_View_Helper_BaseHelper { + + + protected static + $_port, + $_host, + $_memcached; + + + public function Admin_MemcachedStatus() { + $this->_tag('h1', $this->view->titre); + + if ( ! $cache = Storm_Cache::getDefaultZendCache() ) + return $this->_tagWarning($this->_('Memcached n\'est pas utilisé.')); + + if ( $cache instanceof Storm_Cache_Null) + return $this->_tagWarning($this->_('Memcached n\'est pas utilisé.')); + + if ( ! $backend = $cache->getBackend()) + return $this->_tagWarning($this->_('Memcached n\'est pas utilisé.')); + + if ( (! $backend instanceof Zend_Cache_Backend_Memcached) && (! isset(static::$_memcached))) + return $this->_tagWarning($this->_('Memcached n\'est pas utilisé.')); + + $memcached = $this->getMemcached(); + $memcached->addServer($this->getHost(), $this->getPort()); + + $html = [$this->_renderStats($memcached), + $this->_renderSessions($memcached), + $this->_renderLocalStats($memcached)]; + + return $this->_tag('div', implode($html)); + } + + + protected function _renderStats($memcached) { + $stats = $memcached->getStats(); + return + $this->_div(['class' => 'memcached_status_stats'], + $this->_tag('h2', + $this->_('Statistiques globales du server Memcached')) + + . $this->_tag('pre', json_encode($stats, JSON_PRETTY_PRINT))); + } + + + protected function _renderSessions($memcached) { + $keys = $memcached->getAllKeys(); + $session_keys = + array_filter($keys, + function($key) + { + return 0 === strpos($key, 'memc.sess.key'); + }); + $count = count($session_keys); + + $locked_sessions = + array_filter($session_keys, + function($key) + { + return false !== strpos($key, '.lock'); + }); + + $count_locked = count($locked_sessions); + + return + $this->_div(['class' => 'memcached_status_sessions'], + $this->_tag('h2', + $this->_('Toutes les sessions sur le serveur Memcached')) + . $this->_tagNotice($this->_('Nombre de sessions : %s', $count)) + . $this->_tagNotice($this->_('Nombre de sessions bloquée (locked) : %s', $count_locked))); + } + + + protected function _renderLocalStats($memcached) { + $seed = Bokeh_Engine::getInstance()->getCacheSeed(); + $keys = $memcached->getAllKeys(); + + $local_keys = + array_filter($keys, + function($key) use ($seed) + { + return false !== strpos($key, $seed); + }); + + $local_keys = array_values($local_keys); + + $html = []; + $total = 0; + + foreach ($local_keys as $key){ + $cache_content = $memcached->get($key); + + if ( ! $cache_content) { + $html [] = $this->_tagError($this->_('Memcached::get(%s) a retourné le message suivant : %s', + $key, + $memcached->getResultMessage())); + continue; + } + + $cache_content = array_shift($cache_content); + + $size = mb_strlen($cache_content); + + $cache_content = substr($cache_content, 0, 100); + + $html [] = + $this->_tagNotice($this->_('Informations sur la cle %s : ', + $key)) + + . $this->_tagNotice($this->_('Taille de la valeur : %s ko', + number_format(($size / 1024), 2, ',', ' '))) + + . $this->_tag('pre', + $this->_('Les 100 premiers caractères de la valeur: %s', + htmlspecialchars($cache_content))); + + $total += $size; + } + + return + $this->_div(['class' => 'memcached_status_local'], + $this->_tag('h2', + $this->_('Données de ce site dans le Memcached')) + + . $this->_tagNotice($this->_('Clé du site : %s', + Bokeh_Engine::getInstance()->getCacheSeed())) + + . $this->_tagNotice($this->_('Clé du site pour le memcached : %s', + (new Storm_Cache)->getRealSeed())) + + . $this->_tagNotice($this->_('Taille totale des valeurs en cache: %s ko', + (number_format(($total / 1024), 2, ',', ' ')))) + + . $this->_tag('h3', + $this->_('Liste des clés pour ce site')) + + . $this->_tag('pre', + json_encode($local_keys, JSON_PRETTY_PRINT)) + + . $this->_tag('h3', + $this->_('Liste des valeurs pour ce site')) + + . implode($html) + ); + } + + + public function getMemcached() { + return static::$_memcached + ? static::$_memcached + : new \Memcached; + } + + + public function getHost() { + return static::$_host + ? static::$_host + : MEMCACHED_HOST; + } + + + public function getPort() { + return static::$_port + ? static::$_port + : MEMCACHED_PORT; + } + + + public static function setMemcached($memcached) { + static::$_memcached = $memcached; + } + + + public static function setHost($host) { + static::$_host = $host; + } + + + public static function setPort($port) { + static::$_port = $port; + } +} diff --git a/library/startup.php b/library/startup.php index 1a3e32e7d35..d28bce1e658 100644 --- a/library/startup.php +++ b/library/startup.php @@ -42,6 +42,7 @@ class Bokeh_Engine { protected + $_cache_seed, $_warm_up_cache = false, $_config, $_front_controller; @@ -223,7 +224,19 @@ class Bokeh_Engine { $backendOptions); Storm_Cache::setDefaultZendCache($cache); - Storm_Cache::setSeed($this->_config->sgbd->config->dbname.md5(BASE_URL)); + $this->setCacheSeed(md5($this->_config->sgbd->config->dbname . BASE_URL)); + Storm_Cache::setSeed($this->getCacheSeed()); + return $this; + } + + + public function getCacheSeed() { + return $this->_cache_seed;; + } + + + public function setCacheSeed($seed) { + $this->_cache_seed = $seed; return $this; } diff --git a/public/admin/skins/bokeh72/config.json b/public/admin/skins/bokeh72/config.json index 985cad96e82..ae5627df50a 100644 --- a/public/admin/skins/bokeh72/config.json +++ b/public/admin/skins/bokeh72/config.json @@ -58,6 +58,7 @@ "thesaurus_init": "../../images/picto/generation_16.png", "thesaurus_edit": "../../images/picto/generation_16.png", "testmyopac": "icons/menu/link_24.png", + "memcached": "icons/menu/collections_24.png", "home": "../../images/picto/icon_home.gif", "back_to_front": "../../images/picto/profils_16.png", diff --git a/public/admin/skins/bokeh74/config.json b/public/admin/skins/bokeh74/config.json index 46017cdd264..10611bb2cf1 100644 --- a/public/admin/skins/bokeh74/config.json +++ b/public/admin/skins/bokeh74/config.json @@ -61,7 +61,7 @@ "pnb": "../../images/picto/pnb_16.png", "php": "../../images/picto/php.png", - + "memcached": "icons/menu/collections_24.png", "home": "icons/menu/home_24.png", "back_to_front": "icons/menu/site_24.png", diff --git a/public/admin/skins/noel/config.json b/public/admin/skins/noel/config.json index a4bd1988adc..7c29e99825b 100644 --- a/public/admin/skins/noel/config.json +++ b/public/admin/skins/noel/config.json @@ -61,6 +61,7 @@ "pnb": "../../images/picto/pnb_16.png", "php": "../../images/picto/php.png", + "memcached": "icons/menu/collections_24.png", "home": "icons/menu/home_24.png", "back_to_front": "icons/menu/site_24.png", diff --git a/public/admin/skins/retro/config.json b/public/admin/skins/retro/config.json index 3fbb30d1640..6829adb6e2d 100644 --- a/public/admin/skins/retro/config.json +++ b/public/admin/skins/retro/config.json @@ -61,6 +61,7 @@ "pnb": "../../images/picto/pnb_16.png", "php": "../../images/picto/php.png", + "memcached": "icons/menu/collections_24.png", "home": "icons/menu/home_24.png", "back_to_front": "icons/menu/site_24.png", diff --git a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php index 5f31a81f99d..0461d5a690d 100644 --- a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php +++ b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php @@ -88,6 +88,12 @@ class AdminIndexControllerIndexActionTest extends AdminIndexControllerTestCase { public function menuGaucheShouldContainsTestMyOpac() { $this->assertXPathContentContains('//li//a', 'Test de mon OPAC'); } + + + /** @test */ + public function menuGaucheShouldContainsInformationsMemcached() { + $this->assertXPathContentContains('//li//a[contains(@href, "/admin/systeme/memcached-status")]', 'Informations Memcached'); + } } diff --git a/tests/application/modules/admin/controllers/SystemeControllerTest.php b/tests/application/modules/admin/controllers/SystemeControllerTest.php index e85a732560e..4450cf9be0a 100644 --- a/tests/application/modules/admin/controllers/SystemeControllerTest.php +++ b/tests/application/modules/admin/controllers/SystemeControllerTest.php @@ -500,3 +500,153 @@ class Admin_SystemControllerStatusWithErrorDeterminingPublicIpTest extends Admin $this->assertXPathContentContains('//dt[text()="IP publique"]/following-sibling::dd', 'Unable to connect'); } } + + + + +class Admin_SystemControllerNoMemchaedStatusTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/systeme/memcached-status'); + } + + + /** @test */ + public function shouldDisplayTitleMemcachedStatus() { + $this->assertXPathContentContains('//h1', 'État de la configuration et de l\'utilisation de Memcached'); + } + + + /** @test */ + public function shouldDisplayMemcachedIsNotEnabled() { + $this->assertXPathContentContains('//p', 'Memcached n\'est pas utilisé.'); + } +} + + + + +class Admin_SystemControllerMemchaedStatusTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + + $memcached = $this + ->mock() + + ->whenCalled('addServer')->with('localhost', 11211) + ->answers(true) + + ->whenCalled('getStats') + ->answers([[]]) + + ->whenCalled('getAllKeys') + ->answers(['memc.sess.key1', + 'memc.sess.key2', + '12345789237OU8787897', + '123457123982987P89É7', + '1234578UIEUIESRT3242']) + + ->whenCalled('get')->with('12345789237OU8787897') + ->answers(['A1924 A211 A8 M1860 Lfre G3 T1 B19 S1 Y19 V19 B20 E33 Y20 V20 B44 Y44 V44 B23 Y23 V23 S2 HNRNR000', + 787893724789234]) + + ->whenCalled('get')->with('123457123982987P89É7') + ->answers(['a:13:{i:0;a:2:{i:0;i:6;i:1;s:45:"A8 M18435 Lfre G3 T1 B1 S1 E5 Y1 V1 HNRNR0002";}i', + 1231312312313]) + + ->whenCalled('get')->with('1234578UIEUIESRT3242') + ->answers(false) + + ->whenCalled('getResultMessage') + ->answers('NOT STORED') + + ->beStrict(); + + Bokeh_Engine::getInstance()->setCacheSeed('12345'); + Storm_Cache::setSeed('12345'); + + ZendAfi_View_Helper_Admin_MemcachedStatus::setHost('localhost'); + ZendAfi_View_Helper_Admin_MemcachedStatus::setPort(11211); + ZendAfi_View_Helper_Admin_MemcachedStatus::setMemcached($memcached); + + $factory = $this + ->mock() + + ->whenCalled('getBackend') + ->answers($memcached) + + ->whenCalled('load')->with('12345') + ->answers('12345') + + ->beStrict(); + + Storm_Cache::setDefaultZendCache($factory); + + $this->dispatch('/admin/systeme/memcached-status'); + } + + + public function tearDown() { + Storm_Cache::setDefaultZendCache(null); + } + + + /** @test */ + public function shouldDisplayTitleMemcachedStatus() { + $this->assertXPathContentContains('//h1', 'État de la configuration et de l\'utilisation de Memcached'); + } + + + /** @test */ + public function shouldNotDisplayMemcachedIsNotEnabled() { + $this->assertNotXPathContentContains('//p', 'Memcached n\'est pas utilisé.'); + } + + + /** @test */ + public function shouldDisplayNumberOfSessions2() { + $this->assertXPathContentContains('//p', 'Nombre de sessions : 2'); + } + + + /** @test */ + public function shouldDisplayOthers() { + $this->assertXPathContentContains('//pre', '"12345789237OU8787897"'); + } + + + /** @test */ + public function shouldDisplaySeed() { + $this->assertXPathContentContains('//p', 'Clé du site : 12345'); + } + + + /** @test */ + public function shouldDisplayRealSeed() { + $this->assertXPathContentContains('//p', 'Clé du site pour le memcached : 12345'); + } + + + /** @test */ + public function shouldDisplayTotalSize() { + $this->assertXPathContentContains('//p', 'Taille totale des valeurs en cache: 0,17 ko'); + } + + + /** @test */ + public function shouldDisplayValueSize() { + $this->assertXPathContentContains('//p', 'Taille de la valeur : 0,09 ko'); + } + + + /** @test */ + public function memcachedGetShouldReturnAnError() { + $this->assertXPathContentContains('//p[@class="error"]', 'Memcached::get(1234578UIEUIESRT3242) a retourné le message suivant : NOT STORED'); + } +} \ No newline at end of file diff --git a/tests/library/Class/CatalogueTest.php b/tests/library/Class/CatalogueTest.php index fb320bcefbf..f633104bbef 100644 --- a/tests/library/Class/CatalogueTest.php +++ b/tests/library/Class/CatalogueTest.php @@ -544,7 +544,7 @@ class CatalogueTestOAISpec extends ModelTestCase { class CatalogueGetNoticesByPreferencesNotRandomTest extends ModelTestCase { - protected $_cache_key = '25d43b0fa219225c79624883f5f7fd99'; + protected $_cache_key = 'fixed_seed_ddc31b220940c90625375ed2f381bc8c'; public function setUp() { parent::setUp(); diff --git a/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php b/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php index c240c9aa8c0..d7ccd8bc7e5 100644 --- a/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php +++ b/tests/library/ZendAfi/View/Helper/Accueil/SitoTest.php @@ -254,7 +254,7 @@ class SitoViewHelperCachedTest extends SitoViewHelperTestCase { /** @test */ public function cacheShouldBeUse() { - $value = (new Storm_Cache())->getCache()->load('2834982d0d27025a7755ea93669e9537'); + $value = (new Storm_Cache())->getCache()->load('real_seed_6421da151f69b65d9601ce3c3eb18656'); $this->assertNotEquals(false, $value); } } -- GitLab