Skip to content

Commit

Permalink
IcingaRedis: Introduce fetchHostState and fetchServiceState..
Browse files Browse the repository at this point in the history
..as well as `isUnavailable`. Transferred from `VolatileStateResults`
to be re-used outside of it.
  • Loading branch information
nilmerg committed Aug 9, 2023
1 parent cd5f74b commit 90a6878
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 34 deletions.
83 changes: 83 additions & 0 deletions library/Icingadb/Common/IcingaRedis.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Icinga\Module\Icingadb\Common;

use Exception;
use Generator;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Predis\Client as Redis;
Expand Down Expand Up @@ -34,6 +35,22 @@ public static function instance(): self
return self::$instance;
}

/**
* Get whether Redis is unavailable
*
* @return bool
*/
public static function isUnavailable(): bool
{
$self = self::instance();

if ($self->redis === null) {
$self->getConnection();
}

return $self->redisUnavailable;
}

/**
* Get the connection to the Icinga Redis
*
Expand Down Expand Up @@ -95,6 +112,72 @@ public function getConnection(): Redis
return $this->redis;
}

/**
* Fetch host states
*
* @param array $ids The host ids to fetch results for
* @param array $columns The columns to include in the results
*
* @return Generator
*/
public static function fetchHostState(array $ids, array $columns): Generator
{
return self::fetchState('icinga:host:state', $ids, $columns);
}

/**
* Fetch service states
*
* @param array $ids The service ids to fetch results for
* @param array $columns The columns to include in the results
*
* @return Generator
*/
public static function fetchServiceState(array $ids, array $columns): Generator
{
return self::fetchState('icinga:service:state', $ids, $columns);
}

/**
* Fetch object states
*
* @param string $key The object key to access
* @param array $ids The object ids to fetch results for
* @param array $columns The columns to include in the results
*
* @return Generator
*/
protected static function fetchState(string $key, array $ids, array $columns): Generator
{
try {
$results = self::instance()->getConnection()->hmget($key, $ids);
} catch (Exception $_) {
// The error has already been logged elsewhere
return;
}

foreach ($results as $i => $json) {
if ($json !== null) {
$data = json_decode($json, true);
$keyMap = array_fill_keys($columns, null);
unset($keyMap['is_overdue']); // Is calculated by Icinga DB, not Icinga 2, hence it's never in redis

// TODO: Remove once https://github.com/Icinga/icinga2/issues/9427 is fixed
$data['state_type'] = $data['state_type'] === 0 ? 'soft' : 'hard';

if (isset($data['in_downtime']) && is_bool($data['in_downtime'])) {
$data['in_downtime'] = $data['in_downtime'] ? 'y' : 'n';
}

if (isset($data['is_acknowledged']) && is_int($data['is_acknowledged'])) {
$data['is_acknowledged'] = $data['is_acknowledged'] ? 'y' : 'n';
}

yield $ids[$i] => array_intersect_key(array_merge($keyMap, $data), $keyMap);
}
}
}

/**
* Get the last icinga heartbeat from redis
*
Expand Down
49 changes: 15 additions & 34 deletions library/Icingadb/Redis/VolatileStateResults.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace Icinga\Module\Icingadb\Redis;

use Exception;
use Generator;
use Icinga\Application\Benchmark;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Common\IcingaRedis;
Expand All @@ -14,7 +12,6 @@
use ipl\Orm\Query;
use ipl\Orm\Resolver;
use ipl\Orm\ResultSet;
use Predis\Client;
use RuntimeException;

class VolatileStateResults extends ResultSet
Expand All @@ -24,8 +21,8 @@ class VolatileStateResults extends ResultSet
/** @var Resolver */
private $resolver;

/** @var Client */
private $redis;
/** @var bool Whether Redis is unavailable */
private $redisUnavailable;

/** @var bool Whether Redis updates were applied */
private $updatesApplied = false;
Expand All @@ -34,12 +31,7 @@ public static function fromQuery(Query $query)
{
$self = parent::fromQuery($query);
$self->resolver = $query->getResolver();

try {
$self->redis = IcingaRedis::instance()->getConnection();
} catch (Exception $e) {
// The error has already been logged
}
$self->redisUnavailable = IcingaRedis::isUnavailable();

return $self;
}
Expand All @@ -51,12 +43,12 @@ public static function fromQuery(Query $query)
*/
public function isRedisUnavailable(): bool
{
return $this->redis === null;
return $this->redisUnavailable;
}

public function current()
{
if ($this->redis && ! $this->updatesApplied && ! $this->isCacheDisabled) {
if (! $this->redisUnavailable && ! $this->updatesApplied && ! $this->isCacheDisabled) {
$this->rewind();
}

Expand All @@ -65,7 +57,7 @@ public function current()

public function key(): int
{
if ($this->redis && ! $this->updatesApplied && ! $this->isCacheDisabled) {
if (! $this->redisUnavailable && ! $this->updatesApplied && ! $this->isCacheDisabled) {
$this->rewind();
}

Expand All @@ -74,7 +66,7 @@ public function key(): int

public function rewind(): void
{
if ($this->redis && ! $this->updatesApplied && ! $this->isCacheDisabled) {
if (! $this->redisUnavailable && ! $this->updatesApplied && ! $this->isCacheDisabled) {
$this->updatesApplied = true;
$this->advance();

Expand Down Expand Up @@ -134,7 +126,13 @@ protected function applyRedisUpdates()
return;
}

foreach ($this->fetchStates("icinga:{$type}:state", array_keys($states), $keys) as $id => $data) {
if ($type === 'service') {
$results = IcingaRedis::fetchServiceState(array_keys($states), $keys);
} else {
$results = IcingaRedis::fetchHostState(array_keys($states), $keys);
}

foreach ($results as $id => $data) {
foreach ($data as $key => $value) {
$data[$key] = $behaviors->retrieveProperty($value, $key);
}
Expand All @@ -143,7 +141,7 @@ protected function applyRedisUpdates()
}

if ($type === 'service' && ! empty($hostStates)) {
foreach ($this->fetchStates('icinga:host:state', array_keys($hostStates), $hostStateKeys) as $id => $data) {
foreach (IcingaRedis::fetchHostState(array_keys($hostStates), $hostStateKeys) as $id => $data) {
foreach ($data as $key => $value) {
$data[$key] = $behaviors->retrieveProperty($value, $key);
}
Expand All @@ -152,21 +150,4 @@ protected function applyRedisUpdates()
}
}
}

protected function fetchStates(string $key, array $ids, array $keys): Generator
{
$results = $this->redis->hmget($key, $ids);
foreach ($results as $i => $json) {
if ($json !== null) {
$data = json_decode($json, true);
$keyMap = array_fill_keys($keys, null);
unset($keyMap['is_overdue']); // Is calculated by Icinga DB, not Icinga 2, hence it's never in redis

// TODO: Remove once https://github.com/Icinga/icinga2/issues/9427 is fixed
$data['state_type'] = $data['state_type'] === 0 ? 'soft' : 'hard';

yield $ids[$i] => array_intersect_key(array_merge($keyMap, $data), $keyMap);
}
}
}
}

0 comments on commit 90a6878

Please sign in to comment.