Merge pull request #389 from Icinga/reduce-redis-queries-to-a-single-one

Reduce redis queries to a single one
This commit is contained in:
Johannes Meyer 2021-10-08 15:47:11 +02:00 committed by GitHub
commit b04caba2c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 128 additions and 37 deletions

View file

@ -15,6 +15,7 @@ use Icinga\Module\Icingadb\Model\History;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Model\ServicestateSummary;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\Detail\HostDetail;
use Icinga\Module\Icingadb\Widget\Detail\HostInspectionDetail;
@ -38,6 +39,7 @@ class HostController extends Controller
$name = $this->params->getRequired('name');
$query = Host::on($this->getDb())->with(['state', 'icon_image']);
$query->setResultSetClass(VolatileStateResults::class);
$query->getSelectBase()
->where(['host.name = ?' => $name]);
@ -188,6 +190,7 @@ class HostController extends Controller
'host',
'host.state'
]);
$services->setResultSetClass(VolatileStateResults::class);
$services
->getSelectBase()

View file

@ -7,6 +7,7 @@ namespace Icinga\Module\Icingadb\Controllers;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\ItemList\HostList;
use Icinga\Module\Icingadb\Widget\ItemList\HostgroupList;
@ -46,6 +47,7 @@ class HostgroupController extends Controller
$db = $this->getDb();
$hosts = Host::on($db)->with(['state', 'state.last_comment', 'icon_image'])->utilize('hostgroup');
$hosts->setResultSetClass(VolatileStateResults::class);
$hosts->getSelectBase()->where(['host_hostgroup.id = ?' => $this->hostgroup->id]);
$this->applyRestrictions($hosts);

View file

@ -9,6 +9,7 @@ use Icinga\Module\Icingadb\Common\CommandActions;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\HoststateSummary;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Util\FeatureStatus;
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
use Icinga\Module\Icingadb\Web\Controller;
@ -36,6 +37,7 @@ class HostsController extends Controller
$db = $this->getDb();
$hosts = Host::on($db)->with(['state', 'icon_image', 'state.last_comment']);
$hosts->setResultSetClass(VolatileStateResults::class);
$this->handleSearchRequest($hosts);
@ -124,6 +126,7 @@ class HostsController extends Controller
$db = $this->getDb();
$hosts = Host::on($db)->with(['state', 'icon_image']);
$hosts->setResultSetClass(VolatileStateResults::class);
$summary = HoststateSummary::on($db)->with(['state']);
$this->filter($hosts);
@ -194,6 +197,7 @@ class HostsController extends Controller
$db = $this->getDb();
$hosts = Host::on($db)->with('state');
$hosts->setResultSetClass(VolatileStateResults::class);
switch ($this->getRequest()->getActionName()) {
case 'acknowledge':

View file

@ -13,6 +13,7 @@ use Icinga\Module\Icingadb\Common\ServiceLinks;
use Icinga\Module\Icingadb\Hook\TabHook\HookActions;
use Icinga\Module\Icingadb\Model\History;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\Detail\QuickActions;
use Icinga\Module\Icingadb\Widget\Detail\ServiceDetail;
@ -41,6 +42,8 @@ class ServiceController extends Controller
'host',
'host.state'
]);
$query->setResultSetClass(VolatileStateResults::class);
$query->getSelectBase()
->where(['service.name = ?' => $name])
->where(['service_host.name = ?' => $hostName]);

View file

@ -7,6 +7,7 @@ namespace Icinga\Module\Icingadb\Controllers;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\ItemList\ServiceList;
use Icinga\Module\Icingadb\Widget\ItemList\ServicegroupList;
@ -52,6 +53,7 @@ class ServicegroupController extends Controller
'host',
'host.state'
])->utilize('servicegroup');
$services->setResultSetClass(VolatileStateResults::class);
$services->getSelectBase()->where(['service_servicegroup.id = ?' => $this->servicegroup->id]);
$this->applyRestrictions($services);

View file

@ -10,6 +10,7 @@ use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Data\PivotTable;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Model\ServicestateSummary;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use Icinga\Module\Icingadb\Util\FeatureStatus;
use Icinga\Module\Icingadb\Web\Control\ProblemToggle;
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
@ -45,6 +46,7 @@ class ServicesController extends Controller
'host.state',
'icon_image'
]);
$services->setResultSetClass(VolatileStateResults::class);
$this->handleSearchRequest($services);
@ -139,6 +141,7 @@ class ServicesController extends Controller
'host',
'host.state'
]);
$services->setResultSetClass(VolatileStateResults::class);
$summary = ServicestateSummary::on($db)->with(['state']);
$this->filter($services);
@ -214,6 +217,7 @@ class ServicesController extends Controller
'host',
'host.state'
]);
$query->setResultSetClass(VolatileStateResults::class);
$this->handleSearchRequest($query);
@ -317,6 +321,7 @@ class ServicesController extends Controller
'host',
'host.state'
]);
$services->setResultSetClass(VolatileStateResults::class);
switch ($this->getRequest()->getActionName()) {
case 'acknowledge':

View file

@ -1,34 +0,0 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Model\Behavior;
use Exception;
use Icinga\Module\Icingadb\Common\IcingaRedis;
use Icinga\Module\Icingadb\Redis\VolatileState as RedisState;
use ipl\Orm\Contract\RetrieveBehavior;
use ipl\Orm\Model;
class VolatileState implements RetrieveBehavior
{
protected $state;
protected function getVolatileState()
{
if ($this->state === null) {
$this->state = new RedisState(IcingaRedis::instance()->getConnection());
}
return $this->state;
}
public function retrieve(Model $model)
{
try {
$this->getVolatileState()->fetch($model);
} catch (Exception $e) {
// Pass
}
}
}

View file

@ -6,10 +6,8 @@ namespace Icinga\Module\Icingadb\Model;
use Icinga\Module\Icingadb\Model\Behavior\BoolCast;
use Icinga\Module\Icingadb\Model\Behavior\Timestamp;
use Icinga\Module\Icingadb\Model\Behavior\VolatileState;
use ipl\Orm\Behaviors;
use ipl\Orm\Model;
use ipl\Sql\Expression;
/**
* Base class for the {@link HostState} and {@link ServiceState} models providing common columns.
@ -54,7 +52,6 @@ abstract class State extends Model
public function createBehaviors(Behaviors $behaviors)
{
$behaviors->add(new VolatileState());
$behaviors->add(new BoolCast([
'is_problem',
'is_handled',

View file

@ -0,0 +1,109 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Redis;
use Generator;
use Icinga\Application\Benchmark;
use Icinga\Module\Icingadb\Common\IcingaRedis;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use ipl\Orm\Resolver;
use ipl\Orm\ResultSet;
use RuntimeException;
class VolatileStateResults extends ResultSet
{
public function current()
{
if ($this->position === null && ! $this->isCacheDisabled) {
$this->rewind();
}
return parent::current();
}
public function key()
{
if ($this->position === null && ! $this->isCacheDisabled) {
$this->rewind();
}
return parent::key();
}
public function rewind()
{
if ($this->position === null && ! $this->isCacheDisabled) {
$this->advance();
Benchmark::measure('Applying Redis updates');
$this->applyRedisUpdates();
Benchmark::measure('Redis updates applied');
}
parent::rewind();
}
protected function applyRedisUpdates()
{
$type = null;
$behaviors = null;
$states = [];
$hostStates = [];
foreach ($this as $row) {
if ($type === null) {
$behaviors = (new Resolver())->getBehaviors($row->state);
switch (true) {
case $row instanceof Host:
$type = 'host';
break;
case $row instanceof Service:
$type = 'service';
break;
default:
throw new RuntimeException('Volatile states can only be fetched for hosts and services');
}
}
$states[bin2hex($row->id)] = $row->state;
if ($type === 'service' && $row->host instanceof Host) {
$hostStates[bin2hex($row->host->id)] = $row->host->state;
}
}
foreach ($this->fetchStates("icinga:{$type}:state", array_keys($states)) as $id => $data) {
foreach ($data as $key => $value) {
$data[$key] = $behaviors->retrieveProperty($value, $key);
}
$states[$id]->setProperties($data);
}
if ($type === 'service' && ! empty($hostStates)) {
foreach ($this->fetchStates('icinga:host:state', array_keys($hostStates)) as $id => $data) {
foreach ($data as $key => $value) {
$data[$key] = $behaviors->retrieveProperty($value, $key);
}
$hostStates[$id]->setProperties($data);
}
}
}
protected function fetchStates(string $key, array $ids): Generator
{
$results = IcingaRedis::instance()->getConnection()->hmget($key, $ids);
foreach ($results as $i => $json) {
if ($json !== null) {
$data = json_decode($json, true);
$data = array_intersect_key($data, array_flip(VolatileState::$keys));
yield $ids[$i] => $data;
}
}
}
}