feat(scraper): implementar lock distribuído para impedir execuções simultâneas (PAV-119) - #229
Open
RuhanFreitas wants to merge 5 commits into
Open
feat(scraper): implementar lock distribuído para impedir execuções simultâneas (PAV-119)#229RuhanFreitas wants to merge 5 commits into
RuhanFreitas wants to merge 5 commits into
Conversation
## Card - https://linear.app/tatame/issue/PAV-104/melhoria-003-permitir-acesso-a-notificacao-por-meio-do-clique-no ## O que foi feito - Notificações vinculadas a uma vaga (salvamento ou mudança de status) agora são clicáveis: o clique navega até `/dashboard?jobId={id}` e abre automaticamente o modal de detalhes da vaga correspondente. - O `jobId` é resolvido a partir do campo `entityId`/`entityType` já existente na tabela de notificações do backend, então o clique funciona tanto para notificações recém-criadas (locais) quanto para notificações já persistidas e recarregadas da API. - Ao clicar em uma notificação, ela é marcada como lida (chamada a `PATCH /notifications/:id/read`) e passa a ser exibida com um indicador visual diferente (bolinha cinza no lugar da verde), permanecendo na lista em vez de desaparecer. - Notificações sem vínculo a uma vaga (ex.: mensagens de sistema/mentor) continuam sem interação de clique, sem alteração de comportamento. ## Validação ```bash npm run lint --workspace=frontend npm run test --workspace=backend npm run build --workspace=frontend ``` - Testes manuais executados localmente (login, salvar vaga, clicar na notificação, reload de página, reabertura do dropdown) confirmando: - Notificação local recém-criada é clicável imediatamente. - Após reload, a notificação (agora vinda da API) continua clicável. - Ao clicar, o modal da vaga abre e a notificação passa a exibir o indicador de lida, sem sumir da lista. ## Testes adicionados - `tests/unit/new_dashboard/branch-coverage.test.tsx`: clique em notificação com/sem `jobId` (navegação + marcação como lida, e não-interação quando não há vaga associada), fallback de título de rota não mapeada, fechamento de menus ao clicar fora, inclusão de mensagem/notificação local via evento sem depender de reload, e reload via API quando o evento não traz um item. - `tests/unit/new_dashboard/api.test.ts`: chamada à rota `PATCH /notifications/:id/read`, mapeamento de `entityType`/`entityId` para `jobId` (só quando `entityType === "job"`) e de `readAt` para `isRead`. - `tests/unit/new_dashboard/hooks.test.tsx`: cenários de erro combinado ao carregar vagas salvas e recomendadas, ausência de busca inicial de recomendações, atualização de status para vaga inexistente, reaproveitamento de vaga já existente com o mesmo status (sem chamada desnecessária de update), e mensagem de erro padrão quando a rejeição não é uma instância de `Error`. - Cobertura de branches do projeto: 78,35% → 80,1% (mínimo exigido: 80%, conforme `CONTRIBUTING.md`). ## Riscos - Notificações sem `entityType: "job"` (ex.: mensagens de mentor/sistema) permanecem não clicáveis por design, já que não há vaga associada para navegação. Não é uma regressão, é o comportamento esperado dado o escopo da task (front-end). - Nenhuma mudança de contrato de API foi necessária; o backend já expunha os campos usados (`entityType`, `entityId`, `readAt`), então não há risco de quebra em outros consumidores da API. - O pipeline de CI aponta falha em `branch-coverage.test.tsx` e `home.profile.test.tsx` (componente CareerChecklist, não tocado nesta PR) por dependência de data não mockada nos testes: eles esperam o texto fixo "julho de 2026", que deixou de bater assim que o mês virou para agosto. A branch foi aberta em julho, quando os testes ainda passavam; a falha não tem relação com as mudanças desta PR e reproduz da mesma forma na `develop`. Recomendo abrir uma issue separada para mockar a data nesses testes. Os dois testes foram excluídos apenas da execução local usada para validar a cobertura (via filtro `-t`), sem nenhuma alteração no código desses arquivos. ## Evidências - Commits: - `b8deaa7` — feat(frontend): allow user to click on local notifications to navigate through job vacancies - `158487f` — feat(frontend): make notifications click enable and mark them as seen
## Resumo Fecha a PAV-74 repensando a geração de keywords do scraper após o fim do fluxo de submissão de keywords por usuário. ## O que foi feito - Adicionado gerador de keywords no scraper Go, combinando cargo + tecnologia por categoria compatível. - Criado `generator.json` separado de `keywords.json` para versionar a matriz de títulos, tecnologias e categorias. - Mantido fallback interno no Go caso `generator.json` não esteja disponível. - Aplicado `GenerateSearchKeywords` no carregamento padrão, store, scrape e cache key. - Desabilitado o fluxo de submissão de keywords por usuário com `KWSYNC_ENABLED=false`. - `POST /keywords` agora retorna `403` quando o kwsync está desligado. - `GET /keywords` permanece ativo. - `publish` e `publishBatch` não publicam no Valkey quando `KWSYNC_ENABLED=false`. - Consumer Go do `kwsync` não inicia quando a flag está desligada. - Documentada a flag em `.env.example` e `backend/.env.example`. - Adicionados/atualizados testes de backend e scraper. ## Validação - `npm test --workspace=backend` - `go test ./...` dentro de `scraper-go` - `git diff --check`
…e memória do scraper (#226) - feat(scraper): configure global concurrency and runtime limits - Add centralized SCRAPER_MAX_CONCURRENCY configuration with a safe default of 12. - Reject invalid environment values during startup instead of using a silent fallback. - Cap manual and scheduled executions before cache-key and pipeline processing. - Configure Go runtime, CPU, and memory limits for the scraper container. - Add structured logs, defensive validation, tests, and operational documentation. - Refs: PAV-118
## Contexto O `package-lock.json` estava dessincronizado dos manifests do monorepo. Em ambientes baseados em `node:24-alpine`, o `npm ci` falhava com: ```text Missing: yaml@2.9.0 from lock file ``` A dependência `yaml@2.9.0` é requerida de forma transitiva e opcional pelo `lint-staged@17.0.8`, mas sua entrada raiz estava ausente no lockfile. ## Alteração * Regenerado o `package-lock.json` com Node 24 e npm 11. * Adicionada corretamente a resolução de `yaml@2.9.0`. * Normalizados metadados de dependências opcionais de plataforma. * Preservadas as versões declaradas nos manifests. * Nenhum `package.json` foi alterado. * Nenhuma dependência foi atualizada de forma indiscriminada. * Nenhuma alteração funcional foi introduzida. O diff está restrito a: ```text package-lock.json | 79 alterações 16 inserções 63 remoções ``` ## Validações ### Reprodutibilidade * `npm ci` executado duas vezes em ambiente limpo com `node:24-alpine`. * O hash do lockfile permaneceu estável após as duas instalações. * `npm ci` também validado com Node 22 e npm 10, utilizados pelo CI. * `yaml@2.9.0` foi resolvido corretamente. * O `npm ci` não produziu novas alterações no lockfile. ### Testes e builds * Backend: 60 arquivos e 569 testes aprovados. * Frontend: testes aprovados. * Frontend: lint aprovado sem erros e com warnings preexistentes. * Frontend: build aprovado. * Front Admin: 36 arquivos e 107 testes aprovados. * Front Admin: build aprovado. * Scraper Go: testes aprovados. ### Docker Foram construídas com sucesso as imagens: * `migrate` * `backend` * `frontend` * `front_admin` A stack integrada foi iniciada e validada: * PostgreSQL saudável. * Valkey saudável. * Migration concluída com código de saída zero. * Backend saudável em `http://localhost:3001/health`. * Frontend disponível. * Front Admin disponível. * Painel administrativo de observabilidade funcional. Também foi realizada uma coleta controlada para validar a PAV-118: * Limite de aproximadamente 1,5 CPU aplicado ao scraper. * Memória permaneceu abaixo do limite de 2 GiB. * Nenhum reinício do scraper ou backend. * Nenhum panic, fatal, timeout ou OOM identificado. * 862 adapters concluídos e 8 falhas relacionadas a providers externos. * Backend permaneceu saudável após a parada controlada do scraper. * O painel identificou corretamente o scraper como indisponível após a parada. ## Limitações conhecidas O lint do `front_admin` ainda possui quatro erros preexistentes e não relacionados ao lockfile. Seus testes e build foram aprovados. Esses erros não foram corrigidos para preservar o escopo restrito desta entrega. O CI utiliza Node 22, enquanto os Dockerfiles utilizam `node:24-alpine`. O lockfile foi validado nas duas versões, mas o alinhamento e a fixação das versões de Node e npm serão tratados separadamente na evolução do CI/CD. ## Checklist * [x] Lockfile regenerado no ambiente oficial. * [x] Nenhuma edição manual do lockfile. * [x] `yaml@2.9.0` corretamente resolvido. * [x] `npm ci` funciona em Linux/Alpine. * [x] Duas instalações limpas produziram o mesmo resultado. * [x] `npm ci` não modifica o lockfile. * [x] Builds Docker aprovados. * [x] Testes dos workspaces aprovados. * [x] Diff restrito ao `package-lock.json`. * [x] Nenhuma atualização ampla de dependências. * [x] Nenhuma alteração funcional. Fixes PAV-128
…multâneas (PAV-119)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Card
O que foi feito
scraper:run:lock) compartilhado por cron, disparo manual e cache miss de/scrape, com aquisição atômicaSET NX PX, renovação e liberação por token via Lua.409 SCRAPER_ALREADY_RUNNING,503 SCRAPER_RUN_LOCK_UNAVAILABLEe503 SCRAPER_RUN_LOCK_LOST, preservados no backend admin sem auditar disparos rejeitados.SCRAPER_RUN_LOCK_TTL,SCRAPER_RUN_LOCK_RENEW_INTERVAL), estado operacional (runIdseparado do token), cancelamento seguro em perda de lock, espera de liberação no graceful shutdown e documentação/env/Compose.Validação
go test ./...emscraper-gonpm test/ testes debackend/tests/unit/modules/admin/scrapers.test.tsPOST /admin/scraperetorna409e não inicia segunda execuçãoGET scraper:run:lock,PTTL,HGETALL scraper:run:state(sem expor token no state)503 SCRAPER_RUN_LOCK_UNAVAILABLE