Task #6124
closedTask #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
100%
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 fromIIkoEmployeesDownloadCommand.downloadEmployees. -
InitialStatsGatheringCommand—companiesService.findAll(Pageable.from(0, 1000000)). -
CompaniesGarbageCollectionCommand—companiesService.findAll(Pageable.unpaged()), thentryToObsoleteCompanyper company. -
EmployeesCompaniesPartnersDistributionCommand.runOld—employeesService.findAll(Pageable.unpaged())(dead path, but the same shape).
Proposed change¶
- Batch slice persistence: collect the slices for a fetch and
saveAllthem in one statement instead ofmap { 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.
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, и менять их без возможности прогнать на живой базе рискованнее, чем польза.