Пример использования Batch

    Допустим, необходимо проделать некую операцию с большим количеством node и времени выполнения скрипта не хватает.
    В этом случае можно увеличить время выполнения скрипта следующим образом:
    set_time_limit($time); // $time in seconds

    Это, мягко говоря, не самое правильное решение.
    В этом случае на много правильнее реализовать это через batch.


    Пользоваться batch крайне просто. Сейчас приведу пример.

    Допустим мы имеем массив из nid:
    Copy Source | Copy HTML
    1. $nids = array(
    2.   0 => nid,
    3.   1 => nid,
    4.   …
    5.   n => nid,
    6. );


    Также есть некая функция, которая работает с этим массивом.
    Для примера просто будем загружать и сохранять node.

    Copy Source | Copy HTML
    1. function batch_example_nodes_resave($nids = array()){
    2.   foreach ($nids as $nid){
    3.     if (is_numeric($nid)){
    4.       $node = node_load($nid);
    5.       node_save($node);
    6.     }
    7.   }
    8. }


    Теперь описываем функцию с batch.
    Будем делить массив $nids на части (по 5 эллементов) и отправлять в batch_example_nodes_resave()

    Copy Source | Copy HTML
    1. function batch_example_nodes_resave_batch($nids = array()){
    2.   $operations = array();
    3.   while($nids){
    4.     $nids_part = array_splice($nids,  0, 5);
    5.     $operations[] = array('batch_example_nodes_resave', array($nids_part ));
    6.   }
    7.   $batch = array(
    8.     'title' => t('Resave nodes'),
    9.     'operations' => $operations,
    10.   );
    11.   batch_set($batch);
    12.   batch_process();
    13. }


    Теперь достаточно передать наш массив в batch_example_nodes_resave_batch() и посмотреть, как все красиво работает :)

    p.s. Прошу прощения за дабл пост, хабр чето вообще себя не важно чувствует…
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 16
    • +3
      Много. Информативно. С примерами. Дабл пост не нужен был и вовсе.
      • +1
        Спасибо, полезная информация
        • +3
          Callback операции в качестве последнего аргумента должен принимать по ссылке переменную &$context.

          В принципе нет нужды как у Вас через цикл создавать отдельную операцию для каждой ноды.
          Корректнее будет передать в операцию весь массив а в конце выполнения операции устанавливать значение $context['finished'] равное количеству обработанных нод деленному на общую длину массива. Прогресс можно сохранять в массиве $context['sandbox']. Операция будет автоматически вызываться до тех пор пока $context['finished'] не примет в качестве значения единицу.

          Такой подход лучше
          1. С точки зрения идеологии и эстетики: т.к. в батче по хорошему должна быть 1 колбэк — 1 операция (За очень редким исключением. Допустим если надо выполнить операцию А потом Б а потом сново А например)
          2. С точки зрения производительности. Батч сохраняет последовательность операций в БД. А теперь представьте что у Вас рабочая ситуация и ноды достаются из БД в количестве 100500+ штук. Вот у Вас и запишется в ячейку 100500+ операций. А на самом деле нужно записать только 1.
          • +1
            А можно пример? А то с батч не работал, ваши слова звучат умно, но не совсем понятно :(
          • +1
            Надо учесть, что для batch в браузере должна быть открыта страница с запущенной задачей, что не всегда приемлемо, в этом случае лучше юзать очереди.
            Пример нужен более подробный, у вас очень кратко.
            Основная мысль batch — постоянный вызов обработчика для работы над списком задач. То есть нужно показать как брать и сохранять состояния.
            Приведу пример с рабочего проекта (сокращённый), импорт юзеров: pastebin.com/GHkVsaJi
          • 0
            Всегда указывайте версию, даже если не было изменений в api.
            • 0
              Очень нелогичный пример кмк.

              В данном случае лучше было не плодить батч-операции, а сохранять смещение в контексте и использовать одну операцию, в которой смотреть номер шага и брать $nid по смещению (в большинстве случаев это будет SELECT… LIMIT x, y). Разные операции обычно используются для разных действий. Например, resave_node и resave_users — это 2 операции, и каждая операция выполняется в несколько шагов.
              • 0
                3 коммент сверху :)
                • 0
                  >сохранять смещение в контексте и использовать одну операцию, в которой смотреть номер шага и брать $nid по смещению
                  Можно по подробнее, пожалуйста?
                  • +1
                    Представьте что у Вас есть некоторый запрос $sql через который вы получаете массив nid с которыми надо что-то сделать. Тогда перед установкой батча Вы делаете count query к этому запросу получая объем выборки $total. Передаете его в качестве аргумента в операцию.

                    В самой операции делаете свой запрос $sql с LIMIT $a,$a+$k где
                    $a — то сколько вы нод уже обработали (отступ)
                    $k — сколько нод вы хотите обрабатывать за одну итерацию (так чтобы итерация занимала приемлимое для Вас время, например 10 секунд)

                    Потом идет что-то типо такого блока
                    if ($a + $k >= $total) {
                      $context['finished'] = 1;
                    }
                    else {
                    // Тут сохраняем отступ и достаем его потом в начале следущей итерации
                      $context['sandbox']['offset'] = $a + $k;
                    // Эта штука показывает прогресс. Принимает значения [0;1].
                      $context['finished'] = ($a + $k) / $total;
                    }


                    Таким образом операция будет выполняться пока $context['finished'] не станет 1 при этом сообщая батчу о прогрессе после каждой итерации. Обратите внимание на то, что $context['sandbox'] не переходит между операциями.
                • 0
                  А есть примеры/ссылки на саму технику (чтобы реализовать самому такую технологию)? Уж очень не охота в исходниках копаться.
                  • +2
                    Концепция довольно проста.
                    Когда вы устонавливаете задание для батч, задание помещается в БД.
                    Затем происходит переход на страницу с JavaScript кодом который начинает делать последовательные асинхронные запросы на сервер (в друпал к странице example.com/batch) передавая в качестве одного из параметров ID задания.
                    Сервер выполняет часть этого задания (одну операцию или одну одну итерацию) и возвращает назад либо сообщение о том что перация выполнена либо процент выполнения.

                    Таким образом вся магия заключается в том, что большое задание разбивается на несколько этапов и соотвествено несколько запросов, позволяя избежать при этом превышения максимального времени выполнения скрипта.
                    Минус всего этого заключается в том, что при закрытии страничке в браузере с выполняющимся заданием работа механизма прекращается т.к. некому становиться посылать запросы на сервер. Этот эффект можно немного смягчить, если спланировать задание так, чтобы его результат постепенно сохранялся в БД (допустим помечать обработанные ноды, чтобы ни не попали в следущее задание). Тогда можно будет когда нужно прервать задание, а потом безболезненно и без потери результатов запустить его заново.
                    • 0
                      Спасибо, но интересует именно теоретическая основа данной технологии. Т.е. как, например, применить ее к процессу упаковки большого числа файлов? Добавлять по одному? А если попадется 1 большой файл, который не удастся запаковать в один присест?
                      • 0
                        В том то и суть, что если попадается задача, которую нельзя разбить на этапы, подход более чем бесполезен.
                        Конкретно про упаковку файлов могу сказать, что в данном случае чаще всего просто пишется специальная програмка упаковщик. Задание для нее можете заносить в БД через сайт, а програмка сама оттуда будет его получать.
                        Например подобный подход используется на Drupal.org для создания релизов модулей. С некоторой частотой (для дев релизов 2 раза в день, для стабильных релизов раз в 5 минут) программа паковщик проходится по git репозиториям модулей и создает скачиваемый архив.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.