Desafio público da Stone em Go: como praticar validação, concorrência e filas

Estude o desafio público da Stone em Go com limites de tempo, fila cheia e buffer circular. Separe requisitos oficiais de um laboratório original testado.

Author: PracHub

Published: 10/11/2026

Desafio público da Stone em Go: como praticar validação, concorrência e filas

October 11, 2026

Quick Overview

Guia em português do Brasil sobre o repositório público card-interview da Stone, com versão consultada identificada e sem inferir um processo seletivo atual. Um laboratório original de reservas usa relógio fixo, fila limitada e buffer circular pequeno para verificar validação, admissão, processamento, retenção e decisões concorrentes. Nove testes principais com race detector e duas mutações demonstram resultados e limites; não é uma solução pronta para submissão.

Software EngineerFree

O desafio público da Stone em Go fica mais útil para estudar quando você transforma cada requisito em uma pergunta verificável: este timestamp é futuro, esta operação entrou na fila, este resultado ainda está no buffer? Começar pelo número de goroutines deixa essas decisões escondidas. Comece com uma entrada que você consiga acompanhar no papel; transforme sua previsão em teste antes de aumentar a concorrência.

Fato oficial: o repositório stone-payments/card-interview contém um exercício público em três fases. Consultamos o main em 11 de outubro de 2026, no commit 161ec8b36a396bd33c77b2eeb85ca7c256db4f19. Isso confirma o conteúdo dessa versão, não que ela seja a avaliação atual de toda vaga da Stone. Compare o link, a branch e as instruções da sua própria convocação antes de preparar uma entrega.

Não usamos relatos de candidatos para inferir duração, nota de corte ou etapas de contratação. As regras do repositório aparecem identificadas; o laboratório de reservas abaixo é original, com parâmetros e interface diferentes. As recomendações de estudo são nossa interpretação. Os testes locais usam Go 1.27.1, não um ambiente de avaliação da empresa.

Admitir dois pedidos na fila não significa que os dois já foram processados.

Para praticar a parte de retenção depois da leitura, abra Implement a Circular Buffer. Explique a ordem de leitura após a primeira sobrescrita antes de implementar índices.

Identifique o que esta versão pública pede

Resumo dos requisitos oficiais: a primeira fase valida e registra transações, incluindo timestamp RFC3339 que não esteja no futuro. A segunda acrescenta avisos para valor superior a 10.000 e frequência acima de cinco transações em menos de um minuto; suspeita continua aprovada com aviso. A terceira pede retenção das últimas 10.000 transações em buffer circular, workers configuráveis por WORKER_COUNT, fila limitada com padrão de 1.000 e rejeição quando cheia. Também solicita métricas de processamento, rejeição por lotação, uso da fila e workers ativos. Confira o README fixado neste commit para mensagens e formatos exatos.

Não trate lacunas como autorização para inventar uma regra oficial. Registre perguntas sobre precedência de erros, identidade do evento, ordenação e significado da resposta assíncrona. Uma decisão local pode permitir implementar o exercício; precisa continuar marcada como decisão local.

O desafio frontend template-desafio-admin é outro artefato, com outra aplicação. Confirmar a versão evita estudar o problema certo para a convocação errada. Nenhuma regra deste texto substitui instruções recebidas diretamente para sua vaga.

Use um laboratório pequeno para enxergar os limites

Nosso exercício recebe reservas fictícias com ID, Key, Timestamp e valor inteiro em centavos. Não processa cartões nem implementa o payload oficial. Um pedido válido pode entrar numa fila de capacidade dois; o processador conserva três resultados num buffer circular. O aviso aparece acima de 1.000 centavos ou após dois eventos da mesma chave estritamente dentro de vinte segundos.

Esses números são diferentes dos oficiais de propósito. Você consegue listar todos os elementos, forçar lotação sem gerar milhares de entradas e observar a primeira sobrescrita. O resultado serve para estudar mecanismos, não para entregar uma solução pronta em nome próprio.

O contrato original separa três estados: inválido antes da fila, admitido para processamento e processado com ou sem aviso. Um pedido inválido sai antes do envio ao channel: não ocupa a fila e não aumenta o contador reservado à lotação. Aviso não rejeita a reserva. Todas essas escolhas pertencem ao laboratório.

Fixamos o relógio em 2026-10-11T12:00:00Z. Cada job leva um instante de referência explícito, sem chamar time.Now() dentro da validação. Isso permite repetir o mesmo teste amanhã. Para a regra de frequência, o relógio de decisão precisa ser não decrescente por chave; o exercício não resolve eventos processados fora dessa ordem.

Compare instantes, não a aparência das strings

Regra da biblioteca: time.Parse interpreta a entrada conforme o layout informado, e Time.After compara instantes. Use a documentação de time para entender parsing e fusos. O laboratório chama time.Parse(time.RFC3339, ...); não afirma implementar um validador exaustivo de toda a especificação textual RFC3339.

Os casos executados produzem esta matriz:

Timestamp recebidoRelação com o relógio fixoResultado do laboratório
2026-10-11T12:00:00ZExatamente agoraVálido
2026-10-11T09:00:00-03:00Mesmo instante, outro deslocamentoVálido
2026-10-11T11:59:59ZUm segundo antesVálido
2026-10-11T12:00:00.000000001ZUm nanossegundo depoisErro de futuro
2026-10-11T12:00:00Sem fuso exigido pelo layoutErro de parsing
tomorrowNão corresponde ao layoutErro de parsing

Comparar strings faria o deslocamento de fuso participar da decisão de uma forma que não representa o tempo real. Converter para uma data sem horário também perderia o caso de um nanossegundo. Ao explicar o teste, diga qual instante é aceito: exatamente agora passa; um nanossegundo depois é rejeitado.

Testamos ainda valores zero e negativos como inválidos e o aviso monetário em 1.000 versus 1.001 centavos. A unidade inteira facilita enxergar esse limite, mas não implementa câmbio, arredondamento financeiro ou validação de moeda. Se sua versão exigir essas regras, elas merecem contrato e testes próprios.

Faça a janela temporal revelar sua borda

No laboratório, dois eventos anteriores exatamente vinte segundos atrás ficam fora da janela. Dois eventos vinte segundos menos um nanossegundo atrás ficam dentro. Ao chegar o terceiro evento, o primeiro cenário não avisa; o segundo avisa. Não há espera de vinte segundos: os instantes são dados de teste.

A condição executada preserva apenas tempos After(cutoff), sendo cutoff = now - 20s. Isso torna a borda inferior exclusiva. Uma variante defeituosa que incluiu a igualdade falhou no teste da borda exata. Esse teste diferencia duas implementações que parecem equivalentes quando você só usa eventos “recentes”.

A frequência usa o instante de decisão do job, não automaticamente o timestamp informado pelo cliente. Essa é uma escolha original. Qual relógio define uma regra de negócio é uma pergunta diferente de saber fazer parsing. Em produção, horários atrasados, relógios divergentes e reprocessamento podem exigir outra definição; nosso laboratório não os resolve.

Há ainda a atomicidade semântica: contar eventos e acrescentar o atual deve formar uma decisão única. No teste com vinte chamadas concorrentes, mesma chave e mesmo instante, somente as duas primeiras decisões ficam sem aviso; dezoito recebem aviso. Um mutex envolve a contagem, a atualização do histórico e a gravação do resultado.

Proteger apenas o append poderia evitar uma escrita concorrente no slice e ainda permitir que várias chamadas lessem a mesma contagem antiga. “Não houve data race” e “a regra de negócio está correta” são afirmações distintas.

Prove a lotação sem depender de sleep

A regra oficial de linguagem vem do select na especificação Go: um default permite seguir quando nenhuma comunicação está pronta. No laboratório, a admissão tenta enviar ao channel e retorna erro imediatamente se não há espaço. O trecho executado, depois da validação, é:

select {
case p.Jobs <- job:
    return nil
default:
    p.Rejected.Add(1)
    return errors.New("queue full")
}

Para forçar a condição, criamos a fila, enviamos A e B e só depois iniciamos workers. O pedido C encontra capacidade esgotada. Assim, o teste não disputa velocidade com o scheduler e não usa sleep como prova de lotação.

Momento controladoPendentesProcessadosRejeitados por lotaçãoWorkers ativos
A e B admitidos; C rejeitado; workers ainda não iniciados2010
Um worker iniciado, fila fechada pelo dono e drenagem concluída0210

Retornar nil em TrySubmit significa admissão, não conclusão. O laboratório nem sequer expõe um endpoint HTTP. Não traduzimos esse retorno para uma autorização financeira. Se uma API assíncrona precisar permitir consulta posterior, desenhar recibo e estado do processamento é trabalho adicional.

Também não faça um teste if len(ch) < cap(ch) seguido de envio comum como se fosse uma reserva atômica. Outro produtor pode ocupar o espaço entre essas operações. len pode ajudar a observar um estado controlado; não substitui a operação que efetivamente tenta admitir.

Diferencie posição física de ordem de retenção

Um ring de capacidade três começa com A, B e C. Inserir D sobrescreve A; inserir E sobrescreve B. O array físico final é [D, E, C], mas a leitura do mais antigo ao mais novo é [C, D, E].

Após cinco inserções, os slots físicos são D, E e C, enquanto a leitura lógica é C, D e E.

A implementação guarda next, posição da próxima escrita, e size, quantidade efetivamente retida. O início lógico usa (next - size + capacity) % capacity. Isso também funciona antes de o ring ficar cheio, quando size ainda é menor que a capacidade.

O teste verifica a ordem C, D, E e modifica a cópia devolvida por Snapshot. Uma nova leitura continua retornando C no primeiro resultado: o snapshot não expõe o array interno para alteração. Outra variante defeituosa, que devolvia os slots desde o índice zero, falhou após o wrap. A ordem errada mostra resultados fora da sequência prometida; uma referência interna exposta permite que o chamador altere resultados já armazenados.

Com vários workers, a ordem de processamento pode divergir da ordem de chegada. O teste concorrente confere quantidade e capacidade, sem exigir uma sequência de IDs que o contrato não promete. Para exigir ordem por chave, seria necessário projetar essa propriedade; o mutex sozinho não reconstrói a ordem original de admissão.

O ring limita os resultados retidos. Ele não limita todo o consumo de memória. Nosso mapa de histórico por chave não tem política global de remoção de chaves inativas, e uma chave muito ativa pode acumular entradas dentro da janela. Strings dos jobs e goroutines também ocupam memória. Uma frase como “a memória está limitada porque o buffer tem três posições” esconderia essas estruturas.

Encerre os workers com um dono definido

O dono da fila fecha o channel somente depois que todos os produtores terminaram. Os workers percorrem range, processam os itens admitidos e saem quando a fila fechada esvazia. Drain espera o WaitGroup; depois disso verificamos processados, fila vazia e workers ativos zero.

A referência oficial sobre pipelines explica a importância de coordenar encerramento e envios. Neste laboratório não há envio concorrente com close, cancelamento de contexto ou falha de worker. A ordem de chamada faz parte do contrato: juntar produtores, fechar, aguardar consumidores. Transformá-la numa API reutilizável exigiria prevenir chamadas indevidas, não apenas documentá-las.

Um segundo teste usou oito produtores, dez jobs por produtor, quatro workers e capacidade de fila cem. Os oitenta jobs foram processados, sem rejeição por lotação, e somente três resultados permaneceram retidos. Não afirmamos medir throughput ou provar recuperação de crash; esse teste verifica invariantes de execução concorrente dentro de uma carga pequena.

Processed, Rejected e Active são contadores atômicos separados; uma leitura de cada um durante atividade não captura necessariamente o mesmo instante do sistema. Depois de Drain, a execução está estabilizada e podemos comparar o estado final. Para um painel ao vivo, defina precisamente o que “ativo” e “pendente” medem antes de correlacionar números.

O que o resultado dos testes permite afirmar

Executamos nove testes principais, com oito subcasos nomeados, usando go test -race -count=1 -json ./.... Todos passaram; o detector não reportou corridas nos caminhos executados. Em cópias separadas do código, incluímos a igualdade na borda temporal e devolvemos os slots desde o índice zero. TestExclusiveWindow e TestCircularOrderAndSnapshotCopy falharam, respectivamente; a versão original permaneceu intacta.

A documentação oficial do race detector ressalta que ele observa execuções. Não encontra automaticamente problemas em caminhos que seus testes não alcançam. O modelo de memória Go é a referência para justificar sincronização; o fato de um teste passar uma vez não substitui essa justificativa.

Na revisão, separe prova e limite: validamos fronteiras temporais, lotação controlada, retenção e decisões concorrentes; não implementamos HTTP, autenticação, persistência, repetição idempotente nem pagamento real. Não há nota de aprovação da Stone associada ao resultado. Essa precisão ajuda você a explicar o trabalho sem prometer propriedades ausentes.

Pratique cada mecanismo com uma pergunta diferente

Os cinco destinos abaixo foram verificados e servem como exercícios complementares. Não são apresentados como questões aplicadas pela Stone nem como reproduções do seu desafio público.

Pergunta completa no PracHubO que explicar ao resolver
Implement a Circular BufferCompare slots físicos, ordem lógica e primeira sobrescrita.
Implement a fault-tolerant work queue that never loses tasks when workers failIdentifique o que falta numa fila apenas em memória para sobreviver a falhas.
Detect credit-card transaction fraudDefina unidade, igualdade e resultado de cada regra sem confundir aviso e rejeição.
Implement a Rolling-Window Rate LimiterDefenda a borda da janela e a atualização atômica por chave.
Thread-Safe Producer-Consumer Queue: Mutex, Then Spin Lock, Then Lock-FreeDiga quais operações precisam ser sincronizadas e quem encerra a fila.

Comece pelo buffer circular: escreva cinco inserções em três posições e preveja a leitura final. Depois acrescente a fila pequena e teste apenas a lotação, com workers ainda parados. Só então inicie o processamento e confira a retenção; mantenha cada decisão observável. Ao apresentar uma implementação, mostre uma entrada que quebra a versão errada, o teste que detecta a diferença e a propriedade que ainda ficou fora do escopo.

Sources and Further Reading


Comments (0)