Merge pull request #567 from Icinga/handle-redis-errors-gracefully

Handle redis errors gracefully
This commit is contained in:
Johannes Meyer 2022-06-29 13:20:04 +02:00 committed by GitHub
commit 40d66ab804
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 94 additions and 6 deletions

View file

@ -59,7 +59,7 @@ class HostgroupController extends Controller
$paginationControl = $this->createPaginationControl($hosts);
$viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl);
$hostList = (new HostList($hosts))
$hostList = (new HostList($hosts->execute()))
->setViewMode($viewModeSwitcher->getViewMode());
yield $this->export($hosts);

View file

@ -65,7 +65,7 @@ class ServicegroupController extends Controller
$paginationControl = $this->createPaginationControl($services);
$viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl);
$serviceList = (new ServiceList($services))
$serviceList = (new ServiceList($services->execute()))
->setViewMode($viewModeSwitcher->getViewMode());
yield $this->export($services);

View file

@ -4,6 +4,7 @@
namespace Icinga\Module\Icingadb\Redis;
use Exception;
use Generator;
use Icinga\Application\Benchmark;
use Icinga\Module\Icingadb\Common\IcingaRedis;
@ -12,6 +13,7 @@ use Icinga\Module\Icingadb\Model\Service;
use ipl\Orm\Query;
use ipl\Orm\Resolver;
use ipl\Orm\ResultSet;
use Predis\Client;
use RuntimeException;
class VolatileStateResults extends ResultSet
@ -19,6 +21,9 @@ class VolatileStateResults extends ResultSet
/** @var Resolver */
private $resolver;
/** @var Client */
private $redis;
/** @var bool Whether Redis updates were applied */
private $updatesApplied = false;
@ -27,12 +32,28 @@ class VolatileStateResults extends ResultSet
$self = parent::fromQuery($query);
$self->resolver = $query->getResolver();
try {
$self->redis = IcingaRedis::instance()->getConnection();
} catch (Exception $e) {
// The error has already been logged
}
return $self;
}
/**
* Get whether Redis is unavailable
*
* @return bool
*/
public function isRedisUnavailable(): bool
{
return $this->redis === null;
}
public function current()
{
if (! $this->updatesApplied && ! $this->isCacheDisabled) {
if ($this->redis && ! $this->updatesApplied && ! $this->isCacheDisabled) {
$this->rewind();
}
@ -41,7 +62,7 @@ class VolatileStateResults extends ResultSet
public function key(): int
{
if (! $this->updatesApplied && ! $this->isCacheDisabled) {
if ($this->redis && ! $this->updatesApplied && ! $this->isCacheDisabled) {
$this->rewind();
}
@ -50,7 +71,7 @@ class VolatileStateResults extends ResultSet
public function rewind(): void
{
if (! $this->updatesApplied && ! $this->isCacheDisabled) {
if ($this->redis && ! $this->updatesApplied && ! $this->isCacheDisabled) {
$this->updatesApplied = true;
$this->advance();
@ -126,7 +147,7 @@ class VolatileStateResults extends ResultSet
protected function fetchStates(string $key, array $ids, array $keys): Generator
{
$results = IcingaRedis::instance()->getConnection()->hmget($key, $ids);
$results = $this->redis->hmget($key, $ids);
foreach ($results as $i => $json) {
if ($json !== null) {
$data = json_decode($json, true);

View file

@ -7,6 +7,9 @@ namespace Icinga\Module\Icingadb\Widget\ItemList;
use Icinga\Module\Icingadb\Common\BaseItemList;
use Icinga\Module\Icingadb\Common\NoSubjectLink;
use Icinga\Module\Icingadb\Common\ViewMode;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Widget\Notice;
use ipl\Html\HtmlDocument;
abstract class StateList extends BaseItemList
{
@ -18,5 +21,11 @@ abstract class StateList extends BaseItemList
$this->addAttributes(['class' => $this->getViewMode()]);
parent::assemble();
if ($this->data instanceof VolatileStateResults && $this->data->isRedisUnavailable()) {
$this->prependWrapper((new HtmlDocument())->addHtml(new Notice(
t('Icinga Redis is currently unavailable. The shown information might be outdated.')
)));
}
}
}

View file

@ -0,0 +1,31 @@
<?php
/* Icinga DB Web | (c) 2022 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Widget;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
use ipl\Web\Widget\Icon;
class Notice extends BaseHtmlElement
{
/** @var mixed */
protected $content;
protected $tag = 'p';
protected $defaultAttributes = ['class' => 'notice'];
public function __construct($content)
{
$this->content = $content;
}
protected function assemble()
{
$this->addHtml(new Icon('triangle-exclamation'));
$this->addHtml((new HtmlElement('span'))->add($this->content));
$this->addHtml(new Icon('triangle-exclamation'));
}
}

View file

@ -185,6 +185,10 @@ div.show-more {
.show-more {
margin-top: .25em;
}
.notice {
display: none;
}
}
.content > h2:first-child,

View file

@ -0,0 +1,23 @@
// Style
.notice {
@margin: 1em / 1.25;
@padding: .75em / 1.25;
.rounded-corners();
padding: @padding;
color: @text-color-on-icinga-blue;
background-color: @state-warning;
font-weight: bold;
font-size: 1.25em;
// Layout
display: flex;
align-items: baseline;
justify-content: space-between;
margin: 0 @margin @margin @margin;
> span {
.text-ellipsis();
}
}