Перейти к содержанию

Если долго сохраняется элемент в административном разделе

Проблема и её признаки

В этом уроке разберем решение периодически встречающейся проблемы, когда при работе в административном разделе элементы инфоблока сохраняются слишком долго.

Признаки:

  • долгое (60 секунд и выше) сохранение элемента/товара через форму редактирования в административном разделе (при импорте и обновлении из консоли проблема не наблюдается)
  • анализ на стороне хостера выявляет долгий запрос вида:

UPDATE b_iblock_element SET 
  TIMESTAMP_X = TIMESTAMP_X,
  SHOW_COUNTER_START = ifnull(SHOW_COUNTER_START, now()),
  SHOW_COUNTER =  ifnull(SHOW_COUNTER, 0) + 1
WHERE ID=ИД_сохраняемого_элемента
- в некоторых случаях изменения не сохраняются вообще

Наиболее вероятная причина - обработчик событий, посылающий http(s)-запрос к публичной детальной странице элемента, на которой в настройках компонента включено обновление счетчика просмотров.

Далее рассмотрим выявление причины подробнее на примере.

Выявление причины

  1. Выявляем тормозящий запрос.

В ходе первичного анализа установлено, что тормозящий запрос - это вызов метода ClBlockElement::CounterInc. Однако данный метод вызывается только для увеличения счетчика просмотров в публичных компонентах и не должен тормозить процесс сохранение элементов. 2. Проверяем, как работает сохранение элемента.

Открываем код страницы редактирования и видим, что сохранение элемента работает через транзакции. Делается это, чтобы откатить все изменения, если на каком-то этапе сохранения произошла ошибка. 3. Проверяем вызовы методов.

Последовательно проверяем все вызовы методов

CIBlockElement::Update, обновление полей цен и товара, работу с документооборотом. , находящиеся внутри блока транзакции - есть ли на проекте обработчики событий, что именно вызывается в этих методах. Если результат отрицательный - разбираем методы детально и смотрим уже их (например, для метода CIBlockElement::Update таким является вызов CIBlockElement::UpdateSearch, а в нем - CSearch::Index).

Важно! Учтите, в проекте могут быть и другие обработчики с вызовом api.

В результате в одном из обработчиков находим http(s)-запрос к публичной детальной странице элемента. А на этой странице в компоненте

Например, в компонентах Элемент каталога детально или Каталог включена опция Использовать счетчик просмотров. включено обновление счетчика просмотров. Отключаем его - все тормоза исчезают.

Примечание: При открытии транзакции строка (а иногда и вся таблица) блокируется от записи. Это одна из причин, почему использование транзакций запрещено в методах api. Все попытки изменения записи, кроме текущей, ставятся в очередь ожидания на уровне базы. Пока транзакция не закрыта или не отменена - очередь ожидает.

Вывод: при редактировании элемента мы блокируем элемент от других изменений, но обработчик вступает в конфликт (пытается обратиться к обновленному счетчику). В результате элемент, созданный в административной части сайта, сохраняется долго (или может не сохраниться вообще).

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

План сохранения данных без/с обработчиком

Ожидаемый план сохранения данных:

  1. Открываем транзакцию
  2. Обновляем строку в b_iblock_element
  3. Запись блокируется от внешнего изменения (другой хит) на уровне БД
  4. Закрываем транзакцию
  5. Запись разблокируется
  6. Выполняется очередь изменений с других хитов (если успела накопиться)

План сохранения данных с ранее выявленным обработчиком

  1. Открываем транзакцию
  2. Обновляем строку в b_iblock_element
  3. Запись блокируется от внешнего изменения (другой хит) на уровне БД
  4. Вызывается обработчик
  5. Идет хит на публичную страницу
  6. Пытаемся обновить счетчик просмотров (та же самая строка в b_iblock_element)

...долгое ожидание...

  1. Прерывание по таймауту. Обработчик завершен
  2. Если время исполнения скрипта не вышло - закрываем транзакцию. Вышло - просто "падаем"
  3. Запись разблокируется
  4. И тут, наконец, выполнится увеличение счетчика просмотров

Важно! Причины реализации подобных обработчиков могут быть разные. Тем не менее, в идеале на проекте их быть не должно. Это основная и главная рекомендация.

Что делать, если обработчик нельзя убрать

Повторимся, что лучше не использовать в своем проекте обработчики событий, посылающие http(s)-запросы к публичной детальной странице элемента и вступающие в конфликт с выполнением транзакций.

Если по каким-либо причинам от такого обработчика избавиться нельзя, то можно воспользоваться следующими вариантами решения проблемы:

  1. Заменить прямое обращение к публичной странице на добавление в очередь обработки (своя таблица) и агента/скрипт на кроне, который пойдет по очереди и выполнит требуемое (обращение к странице и какие-то действия с результатом). Наиболее предпочтительный способ, однако его реализация требует высокого уровня знаний и квалификации разработчика.
  2. Передавать в строке обращения некий параметр (например,disableCounter=Y). На самой странице параметр вызова компонента USE_ELEMENT_COUNTER (на примере bitrix:catalog или catalog.element) меняем таким образом:
  3. было:

    "USE_ELEMENT_COUNTER" => "Y"
    
    - стало:

    "USE_ELEMENT_COUNTER" => (isset($_REQUEST['disableCounter']) && $_REQUEST['disableCounter'] === 'Y') ? 'N' : 'Y'
    

Этот способ проще первого, однако теряется возможность настройки компонента через визуальный редактор. Это можно поправить переносом правки в шаблон (в случае комплексного компонента).