diff --git a/application/forms/Command/Object/AcknowledgeProblemForm.php b/application/forms/Command/Object/AcknowledgeProblemForm.php index 6fe77923..dd6a1a13 100644 --- a/application/forms/Command/Object/AcknowledgeProblemForm.php +++ b/application/forms/Command/Object/AcknowledgeProblemForm.php @@ -20,8 +20,6 @@ use ipl\Validator\CallbackValidator; use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; use function ipl\Stdlib\iterable_value_first; @@ -220,9 +218,9 @@ class AcknowledgeProblemForm extends CommandForm } $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 250)); + yield $command->setObjects($granted)->setChunkSize(250); } } } diff --git a/application/forms/Command/Object/AddCommentForm.php b/application/forms/Command/Object/AddCommentForm.php index 9bc0942a..9f4ee20f 100644 --- a/application/forms/Command/Object/AddCommentForm.php +++ b/application/forms/Command/Object/AddCommentForm.php @@ -20,8 +20,6 @@ use ipl\Validator\CallbackValidator; use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; use function ipl\Stdlib\iterable_value_first; @@ -163,9 +161,9 @@ class AddCommentForm extends CommandForm } $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 500)); + yield $command->setObjects($granted)->setChunkSize(500); } } } diff --git a/application/forms/Command/Object/CheckNowForm.php b/application/forms/Command/Object/CheckNowForm.php index ac75aa6b..9d278b9d 100644 --- a/application/forms/Command/Object/CheckNowForm.php +++ b/application/forms/Command/Object/CheckNowForm.php @@ -11,8 +11,6 @@ use Icinga\Web\Notification; use ipl\Orm\Model; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; class CheckNowForm extends CommandForm @@ -63,9 +61,9 @@ class CheckNowForm extends CommandForm $command->setForced(); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 1000)); + yield $command->setObjects($granted)->setChunkSize(1000); } } } diff --git a/application/forms/Command/Object/DeleteCommentForm.php b/application/forms/Command/Object/DeleteCommentForm.php index 2fa73661..8a725478 100644 --- a/application/forms/Command/Object/DeleteCommentForm.php +++ b/application/forms/Command/Object/DeleteCommentForm.php @@ -11,8 +11,6 @@ use Icinga\Web\Notification; use ipl\Orm\Model; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; class DeleteCommentForm extends CommandForm @@ -64,9 +62,9 @@ class DeleteCommentForm extends CommandForm $command->setAuthor($this->getAuth()->getUser()->getUsername()); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 500)); + yield $command->setObjects($granted)->setChunkSize(500); } } } diff --git a/application/forms/Command/Object/DeleteDowntimeForm.php b/application/forms/Command/Object/DeleteDowntimeForm.php index 607f74c9..3ee6a062 100644 --- a/application/forms/Command/Object/DeleteDowntimeForm.php +++ b/application/forms/Command/Object/DeleteDowntimeForm.php @@ -11,8 +11,6 @@ use Icinga\Web\Notification; use ipl\Orm\Model; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; class DeleteDowntimeForm extends CommandForm @@ -77,9 +75,9 @@ class DeleteDowntimeForm extends CommandForm $command->setAuthor($this->getAuth()->getUser()->getUsername()); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 250)); + yield $command->setObjects($granted)->setChunkSize(250); } } } diff --git a/application/forms/Command/Object/ProcessCheckResultForm.php b/application/forms/Command/Object/ProcessCheckResultForm.php index 545ace27..fa9833a0 100644 --- a/application/forms/Command/Object/ProcessCheckResultForm.php +++ b/application/forms/Command/Object/ProcessCheckResultForm.php @@ -16,8 +16,6 @@ use ipl\Orm\Model; use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; use function ipl\Stdlib\iterable_value_first; @@ -154,9 +152,9 @@ class ProcessCheckResultForm extends CommandForm $command->setPerformanceData($this->getValue('perfdata')); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 250)); + yield $command->setObjects($granted)->setChunkSize(250); } } } diff --git a/application/forms/Command/Object/RemoveAcknowledgementForm.php b/application/forms/Command/Object/RemoveAcknowledgementForm.php index c66f9d1d..44edae41 100644 --- a/application/forms/Command/Object/RemoveAcknowledgementForm.php +++ b/application/forms/Command/Object/RemoveAcknowledgementForm.php @@ -12,8 +12,6 @@ use Icinga\Web\Notification; use ipl\Orm\Model; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; use function ipl\Stdlib\iterable_value_first; @@ -77,9 +75,9 @@ class RemoveAcknowledgementForm extends CommandForm $command->setAuthor($this->getAuth()->getUser()->getUsername()); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 250)); + yield $command->setObjects($granted)->setChunkSize(250); } } } diff --git a/application/forms/Command/Object/ScheduleCheckForm.php b/application/forms/Command/Object/ScheduleCheckForm.php index 043a8a78..d1edc3ab 100644 --- a/application/forms/Command/Object/ScheduleCheckForm.php +++ b/application/forms/Command/Object/ScheduleCheckForm.php @@ -18,8 +18,6 @@ use ipl\Orm\Model; use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; use function ipl\Stdlib\iterable_value_first; @@ -129,9 +127,9 @@ class ScheduleCheckForm extends CommandForm $command->setCheckTime($this->getValue('check_time')->getTimestamp()); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 1000)); + yield $command->setObjects($granted)->setChunkSize(1000); } } } diff --git a/application/forms/Command/Object/ScheduleServiceDowntimeForm.php b/application/forms/Command/Object/ScheduleServiceDowntimeForm.php index afe578ac..be394406 100644 --- a/application/forms/Command/Object/ScheduleServiceDowntimeForm.php +++ b/application/forms/Command/Object/ScheduleServiceDowntimeForm.php @@ -19,8 +19,6 @@ use ipl\Validator\CallbackValidator; use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; class ScheduleServiceDowntimeForm extends CommandForm @@ -290,9 +288,9 @@ class ScheduleServiceDowntimeForm extends CommandForm } $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 250)); + yield $command->setObjects($granted)->setChunkSize(250); } } } diff --git a/application/forms/Command/Object/SendCustomNotificationForm.php b/application/forms/Command/Object/SendCustomNotificationForm.php index 5f098bc0..d8dabd6d 100644 --- a/application/forms/Command/Object/SendCustomNotificationForm.php +++ b/application/forms/Command/Object/SendCustomNotificationForm.php @@ -17,8 +17,6 @@ use ipl\Orm\Model; use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\Widget\Icon; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; use function ipl\Stdlib\iterable_value_first; @@ -130,9 +128,9 @@ class SendCustomNotificationForm extends CommandForm $command->setAuthor($this->getAuth()->getUser()->getUsername()); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 500)); + yield $command->setObjects($granted)->setChunkSize(500); } } } diff --git a/application/forms/Command/Object/ToggleObjectFeaturesForm.php b/application/forms/Command/Object/ToggleObjectFeaturesForm.php index 296b1041..7f0884e7 100644 --- a/application/forms/Command/Object/ToggleObjectFeaturesForm.php +++ b/application/forms/Command/Object/ToggleObjectFeaturesForm.php @@ -12,8 +12,6 @@ use ipl\Html\FormElement\CheckboxElement; use ipl\Orm\Model; use ipl\Web\FormDecorator\IcingaFormDecorator; use Iterator; -use LimitIterator; -use NoRewindIterator; use Traversable; class ToggleObjectFeaturesForm extends CommandForm @@ -181,11 +179,11 @@ class ToggleObjectFeaturesForm extends CommandForm $command->setEnabled((int) $state); $granted->rewind(); // Forwards the pointer to the first element - while ($granted->valid()) { + if ($granted->valid()) { $this->submittedFeatures[$command->getFeature()] ??= $command->getEnabled(); // Chunk objects to avoid timeouts with large sets - yield $command->setObjects(new LimitIterator(new NoRewindIterator($granted), 0, 1000)); + yield $command->setObjects($granted)->setChunkSize(1000); } } } diff --git a/library/Icingadb/Command/Transport/CommandTransport.php b/library/Icingadb/Command/Transport/CommandTransport.php index 50d358ce..812279d7 100644 --- a/library/Icingadb/Command/Transport/CommandTransport.php +++ b/library/Icingadb/Command/Transport/CommandTransport.php @@ -4,12 +4,12 @@ namespace Icinga\Module\Icingadb\Command\Transport; -use Exception; use Icinga\Application\Config; use Icinga\Application\Logger; use Icinga\Data\ConfigObject; use Icinga\Exception\ConfigurationError; use Icinga\Module\Icingadb\Command\IcingaCommand; +use Icinga\Module\Icingadb\Command\Object\ObjectsCommand; /** * Command transport @@ -103,12 +103,57 @@ class CommandTransport implements CommandTransportInterface public function send(IcingaCommand $command, int $now = null) { $errors = []; + $results = []; $retryCommand = null; foreach (static::getConfig() as $name => $transportConfig) { $transport = static::createTransport($transportConfig); - if ($retryCommand !== null) { + if ($command instanceof ObjectsCommand && $command->getChunkSize() > 0) { + $objects = $command->getObjects(); + + if ($retryCommand !== null) { + try { + $results[] = $transport->send($retryCommand, $now); + } catch (CommandTransportException) { + // It failed prior, so no need to log it again + continue; + } + + $retryCommand = null; + } else { + if ($objects->key() === null) { + // We traverse the iterator manually here, so we have to rewind it before the first iteration. + // That should be the case if the current key is null. May fail if an iterator explicitly yields + // null as the key, but I want to see a justified use case for that… + $objects->rewind(); + } + } + + while ($objects->valid()) { + $batchCommand = clone $command; + $batchCommand->setObjects( + new \LimitIterator(new \NoRewindIterator($objects), 0, $command->getChunkSize()) + ); + + try { + $results[] = $transport->send($batchCommand, $now); + } catch (CommandTransportException $e) { + Logger::error($e); + $errors[] = sprintf('%s: %s.', $name, rtrim($e->getMessage(), '.')); + + $retryCommand = $e->getCommand(); + if ($retryCommand !== null) { + continue 2; + } else { + // Non-recoverable error, so stop trying to send further commands + break 2; + } + } + } + + return $results; + } elseif ($retryCommand !== null) { try { $result = $transport->send($retryCommand, $now); } catch (CommandTransportException) {