Skip to content

Instantly share code, notes, and snippets.

@Geovanek
Last active April 8, 2024 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Geovanek/388bf4c5af2ccffc4bc72548c94222c4 to your computer and use it in GitHub Desktop.
Save Geovanek/388bf4c5af2ccffc4bc72548c94222c4 to your computer and use it in GitHub Desktop.
Job, Performance e atualização em massa no BD.
Estou com um problema de como otimizar uma atualização em massa no BD. Esses dias fiz um script e um job para isso e detonei com o servidor.
Em grande partes, pois estava passando informação em DTO, o que estourou a memória do servidor, consequentemente derrubava o Redis e dava erro 500. Depois o disco encheu tbm. Eram uns 300k de jobs gerados
Em resumo, sorte que estava com um Load Balancer, coloquei outro servidor e derrubei aquele.
Vou tentar explicar o que quero fazer.
Estou implementando uma funcionalidade nova na plataforma para análise dos dados de potência das atividades de ciclismo dos atletas, criar o Perfil de Potência, para tal, gostaria de fazer uma "re-análise" das atividades cadastradas desde 2021.
Minha primeira ideia é pegar todas as atividades de todos os atletas que se encaixem no critério abaixo:
```php
$activities = PerformedActivity::where('device_watts', true)
->where('performed_at', '>=', Carbon::parse('2021-01-01 00:00:00'))
->select(
'id',
'name',
'athlete_id',
'event_type_id',
'has_heartrate',
'performed_at',
)
->with([
'eventType:id,activity_config_id',
'athlete:id' => [
'users:id,sex,birthday',
],
])
->lazy();
```
Aí eu faria uma inserção em massa no BD, minha dúvida é se seria bom fazer isso bia job, tipo usar chunk e disparar jobs para a inserção considerando que devem ter umas 8k de atividades atualmente no BD.
E aí vem minha primeira duvida, como proceder a partir daí?
Pensei em salvar tudo em uma tabela de `tasks`, com os dados básicos e depois ir disparando pouco a pouco via `schedule` os jobs para executarem a análise das atividades.
Basicamente, tentando resumir, a análise da atividade pega os **streams** de FC, Tempo, Potência e Cadência para buscar as melhores médias em diversos intervalos de tempo (1s, 2s, 3s, ... 1h, 2h,...) são 168 `timeSearch` se não me engano.
Isso seria um primeiro **Job** para a primeira análise, pegar as melhores médias.
Consegui otimizar esse Job e esta demorando em torno de 1,6s para fazer toda análise.
Nele eu gero um `Bus::batch`
```php
$BestPowerEffortsJobs[] = new BestPowerEffortJob(
interval: $bestEfforts['interval'],
athleteId: $this->athleteId,
performedActivityId: $activity->id,
activityConfigId: $activity->eventType->activity_config_id,
eventTypeId: $activity->eventType->id,
age: $athleteAge,
sex: $athleteSex,
absoluteValue: $bestEfforts['absoluteValue'],
relativeValue: $bestEfforts['relativeValue'],
startIndex: $bestEfforts['startIndex'],
endIndex: $bestEfforts['endIndex'],
averageHeartrate: $bestEfforts['averageHeartrate'],
peakHeartrate: $bestEfforts['peakHeartrate'],
averageCadence: $bestEfforts['averageCadence'],
performedAt: $activity->performed_at,
);
Bus::batch($BestPowerEffortsJobs)
->name('BestPowerEffortsJob')
->onQueue('trainingAnalysis')
->allowFailures()
->finally(function () use ($athlete, $activity): void {
CheckNewRecordPowerJob::dispatch(
athleteId: $athlete->id,
coachId: $athlete->coach_id,
performedActivityId: $activity->id,
performedActivityName: $activity->name,
performedAt: $activity->performed_at
)->delay(60);
})
->dispatch();
```
E aí são disparados em torno de 150 a 180 jobs, rápidos, 0,6s cada normalmente.
E no final mais um JOB para chegar se é a melhor marca do atleta de todos os tempos ou da temporada.
Se for algum melhor valor, dispara um e-mail.
O problema inicial que eu tive do DTO eu consegui resolver, pois antes mandava mto informação para dentro do Batch (info repetida), puxei isso direto pro JOB onde gero o Batch e ficou bem mais rápido e menos memória (isso em uma única atividade).
A dúvida é como proceder para gerar uma análise em massa?
Vendo esse post do Matheus Guimarães, pensei que utilizar uma tabela de `tasks` seria uma boa opção
https://mateusguimaraes.com/posts/scaling-laravel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment