Make Icinga\Module\Icingadb\Common\IcingaRedis a singleton

This limits the connection attempts and established connections
to a single one. Previously we made as many as we've accessed redis.
This commit is contained in:
Johannes Meyer 2021-09-21 15:22:30 +02:00
parent fcda9bd655
commit 2b480c881d
7 changed files with 39 additions and 33 deletions

View file

@ -20,8 +20,6 @@ use Zend_Validate_Callback;
class RedisConfigForm extends ConfigForm
{
use IcingaRedis;
public function init()
{
$this->setSubmitLabel(t('Save Changes'));
@ -482,7 +480,7 @@ class RedisConfigForm extends ConfigForm
$redisConfig->setSection('redis2', $sections['redis2'] ?? []);
try {
$redis1 = $form->getPrimaryRedis($moduleConfig, $redisConfig);
$redis1 = IcingaRedis::getPrimaryRedis($moduleConfig, $redisConfig);
} catch (Exception $e) {
$form->warning(sprintf(
t('Failed to connect to primary Redis: %s'),
@ -491,19 +489,19 @@ class RedisConfigForm extends ConfigForm
return false;
}
if ($form->getLastIcingaHeartbeat($redis1) === null) {
if (IcingaRedis::getLastIcingaHeartbeat($redis1) === null) {
$form->warning(t('Primary connection established but failed to verify Icinga is connected as well.'));
return false;
}
try {
$redis2 = $form->getSecondaryRedis($moduleConfig, $redisConfig);
$redis2 = IcingaRedis::getSecondaryRedis($moduleConfig, $redisConfig);
} catch (Exception $e) {
$form->warning(sprintf(t('Failed to connect to secondary Redis: %s'), $e->getMessage()));
return false;
}
if ($redis2 !== null && $form->getLastIcingaHeartbeat($redis2) === null) {
if ($redis2 !== null && IcingaRedis::getLastIcingaHeartbeat($redis2) === null) {
$form->warning(t('Secondary connection established but failed to verify Icinga is connected as well.'));
return false;
}

View file

@ -8,11 +8,28 @@ use Exception;
use Icinga\Application\Config;
use Predis\Client as Redis;
trait IcingaRedis
class IcingaRedis
{
/** @var static The singleton */
protected static $instance;
/** @var Redis Connection to the Icinga Redis */
private $redis;
/**
* Get the singleton
*
* @return static
*/
public static function instance(): self
{
if (self::$instance === null) {
self::$instance = new static();
}
return self::$instance;
}
/**
* Get the connection to the Icinga Redis
*
@ -20,7 +37,7 @@ trait IcingaRedis
*
* @throws Exception
*/
public function getIcingaRedis()
public function getConnection()
{
if ($this->redis === null) {
try {
@ -63,8 +80,12 @@ trait IcingaRedis
return $this->redis;
}
public function getLastIcingaHeartbeat(Redis $redis)
public static function getLastIcingaHeartbeat(Redis $redis = null)
{
if ($redis === null) {
$redis = self::instance()->getConnection();
}
// Predis doesn't support streams (yet).
// https://github.com/predis/predis/issues/607#event-3640855190
$rs = $redis->executeRaw(['XREAD', 'COUNT', '1', 'STREAMS', 'icinga:stats', '0']);
@ -90,7 +111,7 @@ trait IcingaRedis
return null;
}
public function getPrimaryRedis(Config $moduleConfig = null, Config $redisConfig = null)
public static function getPrimaryRedis(Config $moduleConfig = null, Config $redisConfig = null)
{
if ($moduleConfig === null) {
$moduleConfig = Config::module('icingadb');
@ -106,14 +127,14 @@ trait IcingaRedis
'host' => $section->get('host', 'localhost'),
'port' => $section->get('port', 6380),
'timeout' => 0.5
] + $this->getTlsParams($moduleConfig));
] + static::getTlsParams($moduleConfig));
$redis->ping();
return $redis;
}
public function getSecondaryRedis(Config $moduleConfig = null, Config $redisConfig = null)
public static function getSecondaryRedis(Config $moduleConfig = null, Config $redisConfig = null)
{
if ($moduleConfig === null) {
$moduleConfig = Config::module('redis');
@ -134,14 +155,14 @@ trait IcingaRedis
'host' => $host,
'port' => $section->get('port', 6380),
'timeout' => 0.5
] + $this->getTlsParams($moduleConfig));
] + static::getTlsParams($moduleConfig));
$redis->ping();
return $redis;
}
private function getTlsParams(Config $config)
private static function getTlsParams(Config $config)
{
$config = $config->getSection('redis');

View file

@ -22,8 +22,6 @@ use ipl\Orm\Model;
abstract class ObjectInspectionDetail extends BaseHtmlElement
{
use IcingaRedis;
protected $tag = 'div';
protected $defaultAttributes = ['class' => ['object-detail', 'inspection-detail']];
@ -101,7 +99,7 @@ abstract class ObjectInspectionDetail extends BaseHtmlElement
$title = new HtmlElement('h2', null, Text::create(t('Volatile State Details')));
try {
$json = $this->getIcingaRedis()
$json = IcingaRedis::instance()->getConnection()
->hGet("icinga:{$this->object->getTableName()}:state", bin2hex($this->object->id));
} catch (Exception $e) {
return [$title, sprintf('Failed to load redis data: %s', $e->getMessage())];

View file

@ -12,14 +12,12 @@ use ipl\Orm\Model;
class VolatileState implements RetrieveBehavior
{
use IcingaRedis;
protected $state;
protected function getVolatileState()
{
if ($this->state === null) {
$this->state = new RedisState($this->getIcingaRedis());
$this->state = new RedisState(IcingaRedis::instance()->getConnection());
}
return $this->state;

View file

@ -15,7 +15,6 @@ use ipl\Stdlib\Filter;
class ApplicationState extends ApplicationStateHook
{
use Database;
use IcingaRedis;
public function collectMessages()
{
@ -52,11 +51,7 @@ class ApplicationState extends ApplicationStateHook
$outdatedDbHeartbeat = $instance->heartbeat < time() - 60;
try {
$redis = $this->getIcingaRedis();
Session::getSession()->getNamespace('icingadb')->delete('redis.down-since');
$lastIcingaHeartbeat = $this->getLastIcingaHeartbeat($redis);
$lastIcingaHeartbeat = IcingaRedis::getLastIcingaHeartbeat();
if ($lastIcingaHeartbeat === null) {
$missingSince = Session::getSession()
->getNamespace('icingadb')->get('redis.heartbeat-missing-since');
@ -93,6 +88,8 @@ class ApplicationState extends ApplicationStateHook
break;
}
Session::getSession()->getNamespace('icingadb')->delete('redis.down-since');
} catch (Exception $e) {
$downSince = Session::getSession()->getNamespace('icingadb')->get('redis.down-since');

View file

@ -13,7 +13,6 @@ use Icinga\Module\Icingadb\Model\Instance;
class RedisHealth extends HealthHook
{
use Database;
use IcingaRedis;
public function getName()
{
@ -23,9 +22,7 @@ class RedisHealth extends HealthHook
public function checkHealth()
{
try {
$redis = $this->getIcingaRedis();
$lastIcingaHeartbeat = $this->getLastIcingaHeartbeat($redis);
$lastIcingaHeartbeat = IcingaRedis::getLastIcingaHeartbeat();
if ($lastIcingaHeartbeat === null) {
$lastIcingaHeartbeat = time();
}

View file

@ -4,14 +4,11 @@
namespace Icinga\Module\Icingadb\Setup;
use Icinga\Module\Icingadb\Common\IcingaRedis;
use Icinga\Module\Icingadb\Forms\RedisConfigForm;
use Icinga\Web\Form;
class RedisPage extends Form
{
use IcingaRedis;
public function init()
{
$this->setName('setup_icingadb_redis');