mbhusty
@mbhusty

Как обработать запрос не получая ошибку по таймауту?

Есть такой кусок кода в одном из методов
getList($params)->fetchAll(),
который достает все необходимые данные из кастомной таблицы.
Соответственно при большом количестве элементов, выполнение скрипта падает по памяти.
Как можно эту проблему решить, при этом получая так же все элементы в итоге? Вариант с увеличением memory_limit не предлагать)

Понятно что нужен какой-то итератор или использовать limit/offset, но не могу понять как.
  • Вопрос задан
  • 150 просмотров
Пригласить эксперта
Ответы на вопрос 2
ipatiev
@ipatiev
Потомок старинного рода Ипатьевых-Колотитьевых
> который достает все необходимые данные из кастомной таблицы.

Ошибка здесь
Количество необходимых элементов для отображения на странице не может быть таким, что при их получении случится таймаут. Человек в состоянии воспринять ну максимум тыщу строк. А лучше - 50.
Поэтому необходимыми являются 50 строк, а не "все".

Если же данные извлекаются для последующей обработки, то надо учитьSQL и обрабатывать данные на стороне БД, получая уже готовый результат

Если уж совсем не получается средствами БД, то тогда это надо делать в консольном скрипте, и не париться по поводу таймаутов.
Ответ написан
@qbudha
Чых-пыхдевелопер
Вы правильно предположили, что нужен итератор. В PHP есть такая штука, как генератор, одна из разновидностей.
Вот, если использовать его, то можно в принципе попробовать уложиться в лимит по памяти.
На своих проектах для полного прохода по записям в БД использую, что-то типа такого:
public function getEntitiesByBatch(int $limit): \Generator
    {
        $expr = $this->createQueryBuilder('s');
        $predicates = $expr->expr()->andX();
        $predicates->add($expr->expr()->isNotNull('s.field1'));
        $predicates->add($expr->expr()->isNotNull('s.field2'));
        $accountState = $expr->expr()->eq('a.field3', 'true');

        $qb = $this->createQueryBuilder('s')
            ->where($predicates)
            ->join('s.account', 'a')
            ->where($accountState)
        ;

        $batches = ceil($qb->select('count(s.id)')->getQuery()->getSingleScalarResult() / $limit);

        for ($batch = 1; $batch <= $batches; ++$batch) {
            yield $this->createQueryBuilder('s')
                ->where($predicates)
                ->join('s.account', 'a')
                ->where($accountState)
                ->setFirstResult($batch * $limit - $limit)
                ->setMaxResults($limit)
                ->getQuery()
                ->toIterable() // тут можно возращать просто массив, но мне нужен итерируемый объект
            ;
        }
    }


Upd 1 А сорян, у вас тут битрикс, может у него есть какие-то дефолтные способы хз. Но общий смысл генераторов от этого не сильно меняется:)

Upd 2 И да, этот код работает в консольном скрипте.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы