Files
request-portal/docs/PLAN.md
Тимур Городецкий 8578653c16 feat: changed get time logic to approvers
2026-08-13 10:01:08 +05:00

10 KiB

План: обсуждение задачи

Создано: 2026-08-12

Контекст

Пишем сюда идею, договорённости и шаги, чтобы ничего не забыть.

Идея (со слов пользователя)

Данные заявок лежат в списках SP Online. Т.к. рекомендуемый предел ~5000, а заявки копятся бесконечно, придумать механизм «архивации»/шардинга: при достижении 5000 элементов заводить новый список с тем же именем + числовой постфикс (— данные_2), и фронт начинает писать новые заявки туда.

Важный факт (уточнение)

  • 5000 = List View Threshold — лимит на строки, сканируемые ОДНИМ запросом, а НЕ на хранение.
  • Список SPO хранит до 30 млн элементов. «Переполнения» по хранению не будет.
  • Реальная проблема = запросы без индекса, сканирующие >5000 строк → throttling. Лечится индексами + фильтрацией, а не разбиением списка.

Цена шардинга по счётчику (что ломается в нашем коде)

  • myRequests.service → чтение становится UNION по N шардам на каждый тип.
  • requestCode.ts → код type-YYMM-itemId, itemId в новом списке снова с 1 → дубли кодов.
  • Списки согласования растут ещё быстрее (строки маршрута) — их тоже шардить?
  • Провижининг схемы/индексов/прав (Sites.Selected) для каждого нового списка на лету.
  • Отчётность/поиск «за период» становятся кросс-списочными.

Варианты (развилка)

  • A. Индексы + пагинация (рекомендую как базу) — индексируем RequestStatus/Created/Author, читаем через .top()+paging. Закрывает throughput-проблему полностью. Кода минимум.
  • B. Архивация по статусу/дате — закрытые заявки переносим в ...— архив, активный список остаётся маленьким. Это «правильная» версия интуиции пользователя. Разгрузка без ломки кодов.
  • C. Шардинг по 5000 (исходная идея) — крайний вариант, только если реально упрёмся в десятки млн.

ВЫБРАННОЕ НАПРАВЛЕНИЕ (после обсуждения)

Причина тормозов подтверждена пользователем: слабый интернет (Казахстан) + список зависает. Есть мандат «держать ≤5000 элементов в списке, плодить новые списки». Решение = C (шардинг) + обязательная пагинация. Обе части нужны:

  • Шардинг ≤5000 на список — выполняет мандат / снимает threshold-throttling (ops).
  • Пагинация + фильтр по индексу — фронт НИКОГДА не качает весь список, только маленькую страницу (.top(50), фильтр по Author/RequestStatus). Именно это убирает зависания на слабом канале. Без этой части шардинг сам по себе скорость НЕ спасёт (4999 строк грузятся так же долго, как 5000).

Кто что создаёт (решено)

  • Создание шардов — бэкенд-скрипт по расписанию (НЕ фронт). Варианты хостинга: Azure Function (timer) / Azure Automation runbook (PnP.PowerShell) / Power Automate / cron на своём сервере (есть git.tnsu.kz → своя инфра). Auth: app-only (сертификат), Sites.Selected.
  • Плюсы: права на создание списков не на фронте; тяжёлый провижининг мимо слабого канала юзера; нет гонок (шард готовится заранее); переиспользуем существующий PnP-провижининг.

Контракт бэк ↔ фронт: РЕЕСТР ШАРДОВ

Маленький SP-список / config-элемент. На каждый тип заявки: базовое имя, номер активного шарда, счётчик, (для approval — свой активный/счётчик).

  • Бэкенд-джоб ОБНОВЛЯЕТ (создал шард → новый активный номер).
  • Фронт ЧИТАЕТ (активный список для записи + полный набор для чтения).

Роли в коде фронта (обёртки, детали позже)

  • resolveActiveList(type) → активный список для записи; все submit.ts зовут её вместо хардкода DATA_LIST_TITLE.
  • resolveAllShards(type) → все шарды для чтения; запрос по каждому с фильтром + .top(), склейка маленьких кусков (быстро на слабом канале).
  • Порог переключения — 4900 (запас до 5000).
  • Коды: внести номер шарда (type-YYMM-s2-itemId), иначе itemId в новом списке снова с 1 → дубли.
  • Списки согласования шардить по СВОЕМУ счётчику (растут в 3-5 раз быстрее) — проектируем после данных.

Открытые вопросы (для следующего обсуждения)

  1. Хостинг бэкенд-джоба: Azure Function / Automation / Power Automate / свой cron (git.tnsu.kz)? Что доступно и кто админит?
  2. Реестр шардов — отдельный SP-список или один JSON config-элемент? (склоняюсь к маленькому списку — виден в админке)
  3. Формат кода с шардом? РЕШЕНО: offset-схема (см. ниже).
  4. Согласование: шардим сразу вместе с данными или сначала только данные, approval потом?
  5. Старые данные >5000 сейчас? РЕШЕНО: таких списков нет, гринфилд. Разовая миграция НЕ нужна. Текущие списки = «шард №1». Настоящая архивация (прятать старые из выдачи) — опционально, потом.
  6. Прогноз объёма: заявок/месяц по каждому типу — чтобы понять реальную частоту переключений.

Решения (зафиксировано)

  • Направление: шардинг + обязательная пагинация (обе части).
  • Создание шардов: бэкенд-скрипт по расписанию (app-only), НЕ фронт.
  • Стык бэк↔фронт: реестр шардов.
  • РЕЖИМ РАБОТЫ: сейчас ТОЛЬКО план и обсуждение. Код НЕ пишем без явного подтверждения пользователя.

Код заявки: offset-схема (сквозная нумерация)

  • ВАЖНО: SP-шный ID мы НЕ контролируем — в каждом новом списке он всегда с 1. Нельзя заставить SP стартовать с 5001. Поэтому сквозную нумерацию делаем в НАШЕМ коде, не в SP ID.
  • Код = type-YYMM-{offset + itemId}, offset = сумма финальных счётчиков всех предыдущих шардов.
    • Шард 1: offset 0 → hr-2608-1 … (совпадает с текущим форматом → обратная совместимость!)
    • Шард 2: offset 5000 → hr-2608-5001 …
  • Уникальность гарантирована. Непрерывность не идеальна (удаления → дырки в SP ID), но это ок — важна уникальность, а не отсутствие пропусков.
  • Финальный счётчик закрытого шарда пишет бэкенд-джоб в реестр при переключении.

«Мои заявки» — из ВСЕХ шардов

  • resolveAllShards(type): опрос всех шардов, каждый с фильтром по Author + .top(), склейка+сортировка.
  • Трафик мал (с каждого шарда пара строк) → быстро на слабом канале.
  • Оптимизация на потом: при большом числе шардов читать по умолчанию только свежие, старые — по кнопке «Показать ещё».

Шаги (черновик, не выполняем)

  • Закрыть открытые вопросы 1-6 (обсуждение)
  • Спроектировать схему реестра шардов
  • Спроектировать интерфейс resolveActiveList / resolveAllShards (без реализации)
  • Спроектировать бэкенд-джоб (псевдокод/алгоритм)
  • Только после «го» — реализация

Заметки

  • Паттерн доступа сегодня: у каждой формы свой DATA_LIST_TITLE, доступ через getSP().web.lists.getByTitle(...) (см. forms/*/submit.ts, load.ts, myRequests.service).