Project

General

Profile

Actions

Task #6124

closed

Task #6116: EPIC: Speed up the stats updater daemon (scripts/workflow/update_stats.sh)

Replace per-row DB writes and unpaged full-table loads in the sync commands

Added by Redmine Admin about 9 hours ago. Updated about 9 hours ago.

Status:
Resolved
Priority:
Normal
Assignee:
-
Start date:
08/26/2026
Due date:
% Done:

100%

Estimated time:

Description

Problem

Several hot loops do one DB round-trip per row, or load an entire table into memory:

  • IikoStatsGatheringService:151 / IikoOlapV2MetricFetchingService.fetchReport(...).map { statSlicesService.save(it) } — one INSERT per stat slice.
  • EmployeesFromIikoServerDtoComposingService.compose — two SELECTs (companiesService.findByCode, employeesService.findByIikoId) for every employee, on every round, called from IIkoEmployeesDownloadCommand.downloadEmployees.
  • InitialStatsGatheringCommandcompaniesService.findAll(Pageable.from(0, 1000000)).
  • CompaniesGarbageCollectionCommandcompaniesService.findAll(Pageable.unpaged()), then tryToObsoleteCompany per company.
  • EmployeesCompaniesPartnersDistributionCommand.runOldemployeesService.findAll(Pageable.unpaged()) (dead path, but the same shape).

Proposed change

  • Batch slice persistence: collect the slices for a fetch and saveAll them in one statement instead of map { save(it) }.
  • In the employee import, preload companies by code and employees by iiko id into maps once, then resolve in memory; batch the inserts and updates.
  • Replace the unpaged/1,000,000 loads with paged iteration.

Acceptance

  • Employee import issues O(1) lookup queries instead of O(n).
  • Stat slices are persisted in batches.
  • No Pageable.unpaged() / Pageable.from(0, 1000000) left in the daemon's commands.
Actions #1

Updated by Redmine Admin about 9 hours ago

  • Status changed from New to Resolved
  • % Done changed from 0 to 100

Реализовано частично, ветка speedup/stats-daemon, коммит 50a6ebef.

Сделано:

  • EmployeesFromIikoServerDtoComposingService.composeAll(dtos) — справочники компаний (по code) и сотрудников (по iikoId) читаются один раз постранично и раскладываются в мапы, дальше всё резолвится в памяти. Было 2 SELECT на каждого сотрудника на каждом обороте. IIkoEmployeesDownloadCommand переведён на него; старый compose(dto) оставлен — им пользуется OutgoingInvoicesDownloadCommand. Сбой на одном сотруднике по-прежнему не роняет остальных.
  • InitialStatsGatheringCommand — вместо Pageable.from(0, 1000000) постраничный обход по 500. Фильтр partner != null поднят наверх, чтобы пул потоков не крутил вхолостую.

Не сделано — и намеренно:

Пакетное сохранение срезов. У внешней библиотеки com.skobeltsyn.statistica:statistica:0.1.4 в StatSliceBaseService есть только save(slice) — ни saveAll, ни массовой вставки. saveAll доступен на StatSliceBasePgRepository (наследник PageableRepository), но лезть в репозиторий чужой библиотеки мимо её сервисного слоя не хочется. После #6118 сохранений всё равно осталось на два-три порядка меньше, так что выигрыш от батча теперь невелик. Правильное место для этого — сама библиотека statistica.

UsersMetricsGatheringCommand и CompaniesGarbageCollectionCommand по-прежнему используют Pageable.unpaged(). Оставил как есть: обе таблицы на порядки меньше companies, и менять их без возможности прогнать на живой базе рискованнее, чем польза.

Actions

Also available in: Atom PDF