Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Добавить поддержку выгрузки большого количества данных без подсчёта элементов (-1) #248

Closed
14 tasks done
mesilov opened this issue Jan 12, 2022 · 3 comments · Fixed by #254
Assignees
Labels
2.x issue related with 2.x sdk version improve DX developer experience improvement
Milestone

Comments

@mesilov
Copy link
Owner

mesilov commented Jan 12, 2022

Документация
https://dev.1c-bitrix.ru/rest_help/rest_sum/start.php

Часто встает задача импорта каких либо сущностей с портала посредством rest. При этом при большом количестве сущностей прямой подход к задаче, с установкой фильтра и передачей в каждый следующий запрос start = start+50, не оптимальный. Так как, при использование start >= 0 на каждый запрос выполняется еще и запрос подсчета количества элементов удовлетворяющих фильтру. Что при большом количестве элементов, попадающих в него, или при сложной фильтрации работает медленно.

Поэтому в случае если вам не нужно количество элементов ( например вам нужно просто 10 последних записей ) или вы делаете импорт всех записей по фильтру, передавайте start = -1.

Данный параметр отключит выполнение запроса подсчета количества элементов и сильно ускорит выборку.

Для выполнения импорта, при этом необходимо будет отсортировать записи по ID и добавить в фильтр условие ID > значения последнего элемента. И с каждым шагом увеличивать его значение. Значение же последнего элемента брать из последнего значения полученного результата.

Условием остановки импорта будет пустой ответ, или то, что в ответе элементов меньше 50.

Функциональные требования

  • в core реализован универсальный метод, данные возвращаются генератором
  • предусмотрены проверки вида: в фильтр не передали ID (монотонно возрастающий)
  • исследован вариант выборки данныых не по ID с проверкой на пустоту колелкции
  • обновлены тесты производительности PerformanceBenchmarks
  • написана документация https://github.com/mesilov/bitrix24-php-sdk/blob/master/docs/RU/Core/Batch/batch-read-mode.md и сделаны выводы

Подготовка окружения

  • демостенд
  • обновить при необходимости генератор демо-данных
  • в логи пишем время выполнения запросов (сеть, работа API)
  • #255
  • обновить PerformanceBenchmarks

Стратегии чтения

  • написана стратегия batch-запросы с подсчётом количества (огорчает модератора)
  • написана стратегия «в лоб» без подсчёта количества
  • написана стратегия без подсчёта количества с минимально-разумными оптимизациями

Опционально

  • #256
@mesilov mesilov self-assigned this Jan 12, 2022
@mesilov mesilov added 2.x issue related with 2.x sdk version improve DX developer experience improvement labels Jan 12, 2022
@mesilov mesilov added this to the 2.x-core milestone Jan 12, 2022
@mesilov mesilov changed the title Добавить поддержку выгрузки большго количества данных без подсчёта элементов (-1) Добавить поддержку выгрузки большого количества данных без подсчёта элементов (-1) Jan 15, 2022
@mesilov mesilov linked a pull request Jan 15, 2022 that will close this issue
@mesilov
Copy link
Owner Author

mesilov commented Jan 16, 2022

Референсы коллег
https://habr.com/ru/post/537694/
image

Общие тезисы

  1. запросы не модифицируют данные, даже если сервер вернёт ошибку, то его можно безболезненно повторить.
  2. есть ограничение на 2 запроса в секунду и пул запросов

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

strategy.1 — ID filter, no batch, no count, order

дефолтная стратегия из документации https://dev.1c-bitrix.ru/rest_help/rest_sum/start.php

Особенности:
— ✅ отключён подсчёт количества элементов в выборке
⚠️ ID элементов в выборке возрастает, т.е. была сделана сортировка результатов по ID
— не используем batch
⚠️ данные из Битрикс24 выбираются отсортированными по "order": {"ID": "ASC"}
— ❗️ парсим ответ сервера для получения следующего ID → проблемы с распараллеливанием запросов
— последовательное выполнение запросов

Задел по оптимизации
— ограниченное использование параллельных запросов

Запросы отправляются к серверу последовательно с параметром "order": {"ID": "ASC"} (сортировка по возрастанию ID), и в каждом последующем запросе используются результаты предыдущего (фильтрация по ID, где ID > максимального ID в результатах предыдущего запроса).

При этом для ускорения используется параметр start = -1 для отключения затратной по времени операции расчета общего количества записей (поле total), которое по умолчанию возвращается в каждом ответе сервера при вызове методов вида *.list.

В потенциале для ускорения можно попытаться параллельно передвигаться по списку сущностей в два потока: с начала списка и с конца, продолжая получать страницы, пока ID в двух потоках не пересекутся. Такой способ, возможно, будет давать двукратное ускорение до тех пор, пока не будет исчерпан пул запросов к серверу и не потребуется включить throttling.

strategy.2 — .list + .get

Особенности:
— ✅ отключён подсчёт количества элементов в основной выборке данных
— ❗️ подсчёт количества элементов используется при формировании списка ID для выборки
⚠️ (не уверен, т.к. это надо только для оптимизации времени работы) ID элементов в выборке возрастает, т.е. была сделана сортировка результатов по ID
— используем batch в основной выборке данных
— последовательное выполнение запросов

Задел по оптимизации
— использование параллельных запросов для batch-комманд

Составная стратегия, при которой при помощи стратегии "Start increment" от сервера получается сначала список всех ID по методу *.list (с указанием, что нужны только ID - 'select': ['ID']) , а потом через метод *.get получается содержимое всех полей для каждого ID. При этом в обоих шагах используются описанные выше способы ускорения "Объединение запросов в батчи" и "Параллельная отправка батчей".

strategy.3 — ID filter, batch, no count, order

— ✅ отключён подсчёт количества элементов в выборке
⚠️ ID элементов в выборке возрастает, т.е. была сделана сортировка результатов по ID
— используем batch
— последовательное выполнение запросов

Задел по оптимизации
— ограниченное использование параллельных запросов

Запросы отправляются к серверу последовательно с параметром "order": {"ID": "ASC"} (сортировка по возрастанию ID).
Т.к. результаты отсортированы по возрастанию ID, то их можно объеденить в батч-запросы с отключённым подсчётом количества элементов в каждом.

Порядок формирования фильтра:

  1. взяли фильтр с «прямой» сортировкой и получили первый ID
  2. взяли фильтр с «обратной» сортировкой и получили последний ID
  3. Т.к. ID монотонно возрастает, то делаем предположение, что все страницы заполнены элементами равномерно, на самом деле там будут «дыры» из-за мастер-мастер репликации и удалённых элементов. т.е. в результирующих выборках не всегда будет ровно 50 элементов.
  4. из готовых фильтров формируем выборки и упаковываем их в батч-команды.
  5. по возможности, батч-запросы выполняются параллельно

strategy.4 — ID filter, MacroSubstitution, batch, no count, order

Вариация strategy.3 c улучшением от Vlad Bazhanov

а какже
filter[>ID] = $result[query_(i-1)][49][ID]

можно сгрупировать в батчи с таким фильтром, только нужно контролировать чтобы в следующей выборке не пришёл ID меньше чем в предыдущей подвыборке (зациклится выборка т.к. $result[query_(i-1)][49][ID] в последнем запросе отдаст что-то около 0, если элементов меньше 50 будет на последнем подзапросе)

@mesilov
Copy link
Owner Author

mesilov commented Jan 16, 2022

Общий подход к реализации в рамках SDK

  • делаем сервис BulkItemsFetcher с зависимостью от Batch
  • делаем отдельные стратегии получения данных для этого фетчера
  • по «дефолту» в сервисах используется наиболее оптимальная с точки зрения производительности.
  • написаны тесты для замеров производительности для всех стратегий.

@mesilov
Copy link
Owner Author

mesilov commented Jan 19, 2022

Доп. тайминги в заголовках

X-Bitrix-Rest-Time — время от захода в модуль рест до точки формирования конечного массива ответа rest, соответствует [time][processing] из ответа

Другие два есть только в облаке и замеряются в техже точках(разница времени от входа в модуль rest и до точки формирования ответа), только из getrusage()
X-Bitrix-Rest-User-Time: ru_utime.tv_sec
X-Bitrix-Rest-System-Time: ru_stime.tv_sec

@mesilov mesilov closed this as completed Feb 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.x issue related with 2.x sdk version improve DX developer experience improvement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant