diff --git a/application/controllers/HistoryController.php b/application/controllers/HistoryController.php index 08a96889b..5435084be 100644 --- a/application/controllers/HistoryController.php +++ b/application/controllers/HistoryController.php @@ -6,9 +6,10 @@ use GuzzleHttp\Psr7\ServerRequest; use Icinga\Module\Icingadb\Model\History; +use Icinga\Module\Icingadb\Util\OptimizerHints; use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; +use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\ItemList\LoadMoreObjectList; use ipl\Stdlib\Filter; use ipl\Web\Control\LimitControl; @@ -87,6 +88,8 @@ public function indexAction() Filter::like('service.id', '*') )); + OptimizerHints::disableOptimizerForHistoryQueries($history); + yield $this->export($history); $this->addControl($sortControl); diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index a12f969e5..b9bce7f47 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Icingadb\Controllers; use ArrayIterator; +use Generator; use Icinga\Exception\NotFoundError; use Icinga\Module\Icingadb\Command\Object\GetObjectCommand; use Icinga\Module\Icingadb\Command\Transport\CommandTransport; @@ -20,6 +21,7 @@ use Icinga\Module\Icingadb\Model\Service; use Icinga\Module\Icingadb\Model\ServicestateSummary; use Icinga\Module\Icingadb\Redis\VolatileStateResults; +use Icinga\Module\Icingadb\Util\OptimizerHints; use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; use Icinga\Module\Icingadb\Web\Controller; @@ -38,7 +40,6 @@ use ipl\Web\Control\SortControl; use ipl\Web\Url; use ipl\Web\Widget\Tabs; -use Generator; class HostController extends Controller { @@ -163,6 +164,8 @@ public function historyAction(): Generator $history->filter(Filter::lessThanOrEqual('event_time', $before)); + OptimizerHints::disableOptimizerForHistoryQueries($history); + yield $this->export($history); $this->addControl($sortControl); diff --git a/application/controllers/NotificationsController.php b/application/controllers/NotificationsController.php index ba805a5a0..971cf3ac0 100644 --- a/application/controllers/NotificationsController.php +++ b/application/controllers/NotificationsController.php @@ -6,10 +6,11 @@ use GuzzleHttp\Psr7\ServerRequest; use Icinga\Module\Icingadb\Model\NotificationHistory; +use Icinga\Module\Icingadb\Util\OptimizerHints; use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions; +use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\ItemList\LoadMoreObjectList; -use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; use ipl\Stdlib\Filter; use ipl\Web\Control\LimitControl; use ipl\Web\Control\SortControl; @@ -81,6 +82,8 @@ public function indexAction() Filter::like('history.service.id', '*') )); + OptimizerHints::disableOptimizerForHistoryQueries($notifications); + yield $this->export($notifications); $this->addControl($sortControl); diff --git a/application/controllers/ServiceController.php b/application/controllers/ServiceController.php index 28abca424..ca40c6843 100644 --- a/application/controllers/ServiceController.php +++ b/application/controllers/ServiceController.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Icingadb\Controllers; use ArrayIterator; +use Generator; use Icinga\Exception\NotFoundError; use Icinga\Module\Icingadb\Command\Object\GetObjectCommand; use Icinga\Module\Icingadb\Command\Transport\CommandTransport; @@ -17,6 +18,7 @@ use Icinga\Module\Icingadb\Model\History; use Icinga\Module\Icingadb\Model\Service; use Icinga\Module\Icingadb\Redis\VolatileStateResults; +use Icinga\Module\Icingadb\Util\OptimizerHints; use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; use Icinga\Module\Icingadb\Web\Controller; @@ -34,7 +36,6 @@ use ipl\Web\Control\SortControl; use ipl\Web\Url; use ipl\Web\Widget\Tabs; -use Generator; class ServiceController extends Controller { @@ -311,6 +312,8 @@ public function historyAction(): Generator $history->filter(Filter::lessThanOrEqual('event_time', $before)); + OptimizerHints::disableOptimizerForHistoryQueries($history); + yield $this->export($history); $this->addControl($sortControl); diff --git a/library/Icingadb/Util/OptimizerHints.php b/library/Icingadb/Util/OptimizerHints.php new file mode 100644 index 000000000..645aafdea --- /dev/null +++ b/library/Icingadb/Util/OptimizerHints.php @@ -0,0 +1,73 @@ +getDb()->getAdapter() instanceof Mysql) { + // Locates the first string column, prepends the optimizer hint, + // and resets columns to ensure it appears first in the SELECT statement: + $q->on(Query::ON_SELECT_ASSEMBLED, static function (Select $select) { + $columns = $select->getColumns(); + foreach ($columns as $alias => $column) { + if (is_string($column)) { + if (Str::startsWith($column, static::DISABLE_OPTIMIZER_HINT)) { + return; + } + + unset($columns[$alias]); + $select->resetColumns(); + + if (is_int($alias)) { + array_unshift($columns, static::DISABLE_OPTIMIZER_HINT . " $column"); + } else { + $columns = [$alias => static::DISABLE_OPTIMIZER_HINT . " $column"] + $columns; + } + + $select->resetColumns()->columns($columns); + + return; + } + } + }); + } + } + + /** + * Determines whether to disable the query optimizer for history queries. + * + * @return bool + */ + protected static function shouldDisableOptimizerForHistoryQueries(): bool + { + return Config::module('icingadb')->get('icingadb', 'disable_optimizer_for_history_queries', false); + } +}