Ускоряем массовое обновление ресурсов в 3 раза

Массовая выгрузка ресурсов, не такая уж простая задача, как может показаться на первый взгляд. Много подводных камней, касающихся улучшения производительности скрипта. Особенно неприятно, когда тормозит скорость работы из-за функционала ядра MODX.


Так, например, я столкнулся с куском кода в ядре, который увеличивает время выгрузки большого кол-ва товаров в ~3 раза, выполняя одно и то же ресурсоёмкое действие каждый раз, когда скрипт запускает обновление ресурса через процессор modResourceUpdateProcessor.

Сразу оговорю тот момент, что я не считаю этот кусок кода в ядре лишним или вредным, нет! Я полагаю, что этот код при обновлении большого кол-ва ресурсов можно выполнить самостоятельно по окончании работы скрипта выгрузки.

Я говорю вот об этой строчке кода. Давайте попробуем разобраться, что же с ней не так!

Детали проблемы


А всё дело в том, что запускается там метод modX::reloadContext(), который в свою очередь вызывает метод modContext::prepare(), а он уже запускает метод modCacheManager::generateContext(). Вот тут и начинается самое интересное!

Незамысловатый дебаг показал, что в методе modCacheManager::generateContext() вот здесь и здесь основной затык по времени. Там MODX получает и генерирует заново всю карту ресурсов. Представьте, если у нас 100k товаров на сайте, сколько времени потребуется, чтобы собрать всю эту карту из базы? На нормальном сервере карта из 100k ресурсов генерируется в течение 0,3-0,5 сек. А теперь подумайте, надо ли это делать при каждом обновлении ресурса во время массовой выгрузки?

Приведу пример


У нас на выгрузке ежедневно 50k ресурсов, допустим, все их нужно обновить. Обновление 1 ресурса с запуском метода modX::reloadContext() в этом случае занимает ~0,6 сек. А 50k ресурсов будет обновляться ~8 часов.

Это если производить выгрузку, что называется, в вакууме, не учитывая скачивание изображений и загрузку их для каждого товара в miniShop2 галерею.

Теперь давайте закомментируем этот метод в ядре и снова замерим скорость работы. Обновление ресурса без метода modX::reloadContext() происходит за 0,2 сек. При 50k товарах выгрузка «в вакууме» займёт ~3 часа. После чего, мы спокойно можем выполнить один раз тот-же самый $modx->reloadContext() у себя в скрипте.

Как решить?


Для себя я обошёл это, вставив в процессор msProductUpdateProcessor компонента miniShop2, в самый конец класса такой код:
public function cleanup()
{
    $this->object->removeLock();
    $this->clearCache();

    $returnArray = $this->object->get(array_diff(array_keys($this->object->_fields), array('content','ta','introtext','description','link_attributes','pagetitle','longtitle','menutitle','properties')));
    foreach ($returnArray as $k => $v) {
        if (strpos($k,'tv') === 0) {
            unset($returnArray[$k]);
        }
    }
    $returnArray['class_key'] = $this->object->get('class_key');
    $this->workingContext->prepare(false);
    
    if ($this->getProperty('_reloadContext', true)) {
        $this->modx->reloadContext($this->workingContext->key);
    }
    
    $returnArray['preview_url'] = '';
    if (!$this->object->get('deleted')) {
        $returnArray['preview_url'] = $this->modx->makeUrl($this->object->get('id'), $this->object->get('context_key'), '', 'full');
    }

    return $this->success('',$returnArray);
}
Т.к. он расширяет процессор modResourceUpdateProcessor, я переназначил метод cleanup(), в котором вызывается modX::reloadContext(). Теперь мы можем при обновлении через процессор передать параметр _reloadContext = false и метод, тормозящий массовую выгрузку в 3 раза, запускаться не будет.
Главное, теперь после обновления miniShop2 нужно заново накатывать этот код!
Павел Гвоздь
11 сентября 2018, 14:29
18
381
+19

Комментарии: 5

Павел
12 сентября 2018, 10:10
0
Вот почему оказывается такие тормоза при сохранении ресурса
Денис
13 сентября 2018, 09:44
0
Спасибо огромное за анализ проблемы. На одном проекте тоже возникала проблема с тормозами, все никак руки не доходили разобраться в причине, думал что какая то карта генерируется при каждом обновлении, поскольку тормозить начинало с увеличением количества товаров.
Наумов Алексей
13 сентября 2018, 09:59
0
Мне при схожей задаче пришлось из-за этого уйти от обновления ресурсов через процессоры, т.к. очень долго.
Все через ручную работу с объектами делал и каждый раз вспоминал, не забыл ли чего…
Зато 15 тыс объектов за 5 минут обновлялись.
    Павел Гвоздь
    13 сентября 2018, 10:02
    0
    К сожалению, не универсально… накатил какой-то пакет, который использует события обновления ресурса и всё по швам пошло.
      Наумов Алексей
      13 сентября 2018, 11:06
      0
      Именно… приходится все помнить и держать в голове постоянно эту вероятность.