Este desafio simula um sistema de auditoria de logs que leva 45 minutos para processar dados. O objetivo é criar um motor de agregação em Go demonstrando domínio progressivo de concorrência através de quatro implementações: sequencial, concorrente ingênua (com race conditions), com Mutex, e usando Worker Pool com Channels, conceitos essenciais para sistemas de alta performance.
Materiais para você usar como base para o desenvolvimento
Estrutura, regras e requisitos do projeto
Nesse seguinte cenário hipotético temos uma equipe de Business Intelligence (BI) precisa de um relatório de auditoria de todos os eventos da última hora. O problema? O job atual que processa esses dados está demorando 45 minutos para rodar. Ninguém consegue trabalhar assim.
Sua Missão: É criar do zero um novo motor de agregação em Go para resolver esse gargalo de uma vez por todas. Nossos servidores despejam milhares de arquivos de log JSON em um diretório de logs. Sua tarefa é usar o arsenal de concorrência do Go (Goroutines, Channels e Mutexes) para implementar esse job de agregação.
O objetivo aqui é que você demonstre sua habilidade em aplicar os seguintes conceitos:
go run -race .) antes mesmo que ele corrompa dados em produção. Este é o tipo de bug que derruba sistemas.sync.Mutex para proteger seções críticas. E, mais importante, saber quando não usar. Você deve entender e medir o custo da Contenção de Lock — o ponto onde sua solução "concorrente" se torna mais lenta que a síncrona porque todas as goroutines estão paradas em uma fila.sync.WaitGroup e o fechamento de channels para garantir que o sistema desligue de forma limpa, sem perder um único evento que estava "em trânsito".Você deve criar um arquivo main.go que implemente as 4 funções a seguir. Na sua função main, você deve:
GenerateMockFiles fornecida).time.Since(start)) de cada uma e imprimir os resultados para comparação.Implemente a versão mais simples e direta. Ela deve processar um arquivo de cada vez, sequencialmente, na goroutine principal.
func ProcessSequential(files []string) *ReportReport usando NewReport().for...range padrão sobre a lista de files.bufio.Scanner).Event e chame report.AddEvent(event).report.AddError().Agora, vamos tentar "acelerar" as coisas processando cada arquivo em sua própria Goroutine. Esta implementação é intencionalmente falha.
func ProcessConcurrentNaive(files []string) *ReportReport compartilhado.sync.WaitGroup para rastrear as goroutines.files. Para cada arquivo:
wg.Add(1)go func(filename string) { ... }.defer wg.Done(), processe o arquivo (abrir, ler, decodificar).report.AddEvent(event).report.AddError().wg.Wait() para esperar todas as goroutines terminarem.Agora que você viu o problema, corrija-o usando a abordagem de "memória compartilhada protegida".
func ProcessConcurrentMutex(files []string) *ReportWaitGroup e goroutines).AddEvent e AddError, você deve chamar as versões seguras: report.AddEventSafe(event) e report.AddErrorSafe().Esta é a arquitetura mais escalável. Vamos refatorar para um padrão que evita a memória compartilhada, seguindo o lema do Go: "Não comunique compartilhando memória; compartilhe memória comunicando."
func ProcessPipeline(files []string, numWorkers int) *Reportjobs := make(chan string, len(files))results := make(chan ProcessResult, 1000) (Use uma struct ProcessResult que possa conter ou um Event, ou um error).sync.WaitGroup para os workers.numWorkers goroutines.filename do canal jobs (for filename := range jobs).ProcessResults para o canal results.NewReport().results (for res := range results).ProcessResult: se res.Err != nil, chame report.AddError(); senão, chame report.AddEvent(res.Event). (Note que usamos as versões sem lock, pois esta é a única goroutine que modifica o report).files para o canal jobs.close(jobs) (Sinaliza aos workers que não há mais trabalho).wg.Wait()).close(results) (Sinaliza ao agregador que não há mais resultados).done ou outro WaitGroup) e então retorne o report.go run -race . deve passar limpa (sem avisos) para as Partes 1, 3 e 4.go run -race . na Parte 2 deve obrigatoriamente detectar e reportar a "DATA RACE". Você deve ser capaz de apontar no código exatamente onde e por que ela ocorre.time.Since(start) ou o pacote testing) que comprovem a melhoria de desempenho das Partes 3 e 4 em relação à baseline (Parte 1) usando um conjunto de dados significativo (ex: 100 arquivos, 10.000 linhas/arquivo).sync.Mutex (ou sync.RWMutex) para garantir a segurança.Nesse seguinte cenário hipotético temos uma equipe de Business Intelligence (BI) precisa de um relatório de auditoria de todos os eventos da última hora. O problema? O job atual que processa esses dados está demorando 45 minutos para rodar. Ninguém consegue trabalhar assim.
Sua Missão: É criar do zero um novo motor de agregação em Go para resolver esse gargalo de uma vez por todas. Nossos servidores despejam milhares de arquivos de log JSON em um diretório de logs. Sua tarefa é usar o arsenal de concorrência do Go (Goroutines, Channels e Mutexes) para implementar esse job de agregação.
O objetivo aqui é que você demonstre sua habilidade em aplicar os seguintes conceitos:
go run -race .) antes mesmo que ele corrompa dados em produção. Este é o tipo de bug que derruba sistemas.sync.Mutex para proteger seções críticas. E, mais importante, saber quando não usar. Você deve entender e medir o custo da Contenção de Lock — o ponto onde sua solução "concorrente" se torna mais lenta que a síncrona porque todas as goroutines estão paradas em uma fila.sync.WaitGroup e o fechamento de channels para garantir que o sistema desligue de forma limpa, sem perder um único evento que estava "em trânsito".Você deve criar um arquivo main.go que implemente as 4 funções a seguir. Na sua função main, você deve:
GenerateMockFiles fornecida).time.Since(start)) de cada uma e imprimir os resultados para comparação.Implemente a versão mais simples e direta. Ela deve processar um arquivo de cada vez, sequencialmente, na goroutine principal.
func ProcessSequential(files []string) *ReportReport usando NewReport().for...range padrão sobre a lista de files.bufio.Scanner).Event e chame report.AddEvent(event).report.AddError().Agora, vamos tentar "acelerar" as coisas processando cada arquivo em sua própria Goroutine. Esta implementação é intencionalmente falha.
func ProcessConcurrentNaive(files []string) *ReportReport compartilhado.sync.WaitGroup para rastrear as goroutines.files. Para cada arquivo:
wg.Add(1)go func(filename string) { ... }.defer wg.Done(), processe o arquivo (abrir, ler, decodificar).report.AddEvent(event).report.AddError().wg.Wait() para esperar todas as goroutines terminarem.Agora que você viu o problema, corrija-o usando a abordagem de "memória compartilhada protegida".
func ProcessConcurrentMutex(files []string) *ReportWaitGroup e goroutines).AddEvent e AddError, você deve chamar as versões seguras: report.AddEventSafe(event) e report.AddErrorSafe().Esta é a arquitetura mais escalável. Vamos refatorar para um padrão que evita a memória compartilhada, seguindo o lema do Go: "Não comunique compartilhando memória; compartilhe memória comunicando."
func ProcessPipeline(files []string, numWorkers int) *Reportjobs := make(chan string, len(files))results := make(chan ProcessResult, 1000) (Use uma struct ProcessResult que possa conter ou um Event, ou um error).sync.WaitGroup para os workers.numWorkers goroutines.filename do canal jobs (for filename := range jobs).ProcessResults para o canal results.NewReport().results (for res := range results).ProcessResult: se res.Err != nil, chame report.AddError(); senão, chame report.AddEvent(res.Event). (Note que usamos as versões sem lock, pois esta é a única goroutine que modifica o report).files para o canal jobs.close(jobs) (Sinaliza aos workers que não há mais trabalho).wg.Wait()).close(results) (Sinaliza ao agregador que não há mais resultados).done ou outro WaitGroup) e então retorne o report.go run -race . deve passar limpa (sem avisos) para as Partes 1, 3 e 4.go run -race . na Parte 2 deve obrigatoriamente detectar e reportar a "DATA RACE". Você deve ser capaz de apontar no código exatamente onde e por que ela ocorre.time.Since(start) ou o pacote testing) que comprovem a melhoria de desempenho das Partes 3 e 4 em relação à baseline (Parte 1) usando um conjunto de dados significativo (ex: 100 arquivos, 10.000 linhas/arquivo).sync.Mutex (ou sync.RWMutex) para garantir a segurança.Use este checklist para ajudar a organizar a sua entrega
Confira os resultados esperados do projeto

Envie o projeto para ver a resolução
Ao enviar seu projeto, você poderá conferir os resultados esperados