fix(convex): adicionar logs obrigatorios em cron jobs para evitar shape_inference errors
- Adicionar console.log no inicio de autoEndInactiveSessions (liveChat.ts) - Adicionar console.log no inicio de cleanupStalePendingPolicies (usbPolicy.ts) - Documentar problema de shape_inference e solucao em OPERATIONS.md (Secao 11) - Atualizar .env.example com BETTER_AUTH_SECRET de 32+ caracteres O shape_inference do Convex self-hosted falha ao unificar arrays vazios (logLines: []) com arrays de strings (logLines: ["msg"]). Garantindo que todo cron job produza ao menos um log, evitamos o conflito de tipos. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
48d9de8dd1
commit
e2dde8510a
4 changed files with 162 additions and 1 deletions
|
|
@ -5,7 +5,7 @@ NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||||
|
|
||||||
# Better Auth
|
# Better Auth
|
||||||
BETTER_AUTH_URL=http://localhost:3000
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
BETTER_AUTH_SECRET=change-me-in-prod
|
BETTER_AUTH_SECRET=your-secret-key-at-least-32-chars-long
|
||||||
|
|
||||||
# Convex (dev server URL)
|
# Convex (dev server URL)
|
||||||
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
|
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
|
||||||
|
|
|
||||||
|
|
@ -747,6 +747,8 @@ const INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000
|
||||||
export const autoEndInactiveSessions = mutation({
|
export const autoEndInactiveSessions = mutation({
|
||||||
args: {},
|
args: {},
|
||||||
handler: async (ctx) => {
|
handler: async (ctx) => {
|
||||||
|
// Log obrigatorio para evitar shape_inference errors com logLines vazios
|
||||||
|
console.log("cron: autoEndInactiveSessions iniciado")
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const cutoffTime = now - INACTIVITY_TIMEOUT_MS
|
const cutoffTime = now - INACTIVITY_TIMEOUT_MS
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,8 @@ export const cleanupStalePendingPolicies = mutation({
|
||||||
staleThresholdMs: v.optional(v.number()),
|
staleThresholdMs: v.optional(v.number()),
|
||||||
},
|
},
|
||||||
handler: async (ctx, args) => {
|
handler: async (ctx, args) => {
|
||||||
|
// Log obrigatorio para evitar shape_inference errors com logLines vazios
|
||||||
|
console.log("cron: cleanupStalePendingPolicies iniciado")
|
||||||
const thresholdMs = args.staleThresholdMs ?? 3600000 // 1 hora por padrao
|
const thresholdMs = args.staleThresholdMs ?? 3600000 // 1 hora por padrao
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const cutoff = now - thresholdMs
|
const cutoff = now - thresholdMs
|
||||||
|
|
|
||||||
|
|
@ -236,3 +236,160 @@ Resumo das mudanças aplicadas no painel administrativo para simplificar “Usu
|
||||||
- Agentes (dispositivos): provisionamento automático; edição detalhada/vínculo principal em Admin ▸ Dispositivos. Arquivo: `src/components/admin/devices/admin-devices-overview.tsx`.
|
- Agentes (dispositivos): provisionamento automático; edição detalhada/vínculo principal em Admin ▸ Dispositivos. Arquivo: `src/components/admin/devices/admin-devices-overview.tsx`.
|
||||||
|
|
||||||
> Observação operacional: mantivemos o provisionamento de dispositivos inalterado (token/e‑mail técnico), e o acesso web segue apenas para pessoas. A unificação é de UX/gestão.
|
> Observação operacional: mantivemos o provisionamento de dispositivos inalterado (token/e‑mail técnico), e o acesso web segue apenas para pessoas. A unificação é de UX/gestão.
|
||||||
|
|
||||||
|
## 10) Convex Self-Hosted — Otimizacao de Memoria (OOM)
|
||||||
|
|
||||||
|
### Problema
|
||||||
|
|
||||||
|
O Convex self-hosted carrega **todas as versoes de todos os documentos** em memoria (SQLite in-memory). Com heartbeats de dispositivos a cada 30s, cada `patch()` criava uma nova versao do documento `machines`, causando:
|
||||||
|
|
||||||
|
- Acumulo exponencial: ~3500 versoes para ~65 maquinas
|
||||||
|
- Uso de memoria crescente: 8-10 GiB para poucos dispositivos
|
||||||
|
- Crashes por OOM e desconexoes WebSocket (codigo 1006)
|
||||||
|
|
||||||
|
### Solucao Implementada (2025-12-09)
|
||||||
|
|
||||||
|
**1. Separacao de heartbeats em tabela dedicada**
|
||||||
|
|
||||||
|
Nova tabela `machineHeartbeats` armazena apenas `lastHeartbeatAt`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// convex/schema.ts
|
||||||
|
machineHeartbeats: defineTable({
|
||||||
|
machineId: v.id("machines"),
|
||||||
|
lastHeartbeatAt: v.number(),
|
||||||
|
}).index("by_machine", ["machineId"]),
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Heartbeat inteligente**
|
||||||
|
|
||||||
|
A funcao `heartbeat` agora:
|
||||||
|
- SEMPRE atualiza `machineHeartbeats` (documento pequeno, upsert)
|
||||||
|
- SO atualiza `machines` quando ha mudancas reais (hostname, OS, metadata, status)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Verificar se ha mudancas reais nos dados
|
||||||
|
const hasMetadataChanges = Object.keys(metadataPatch).length > 0
|
||||||
|
const hasHostnameChange = args.hostname && args.hostname !== machine.hostname
|
||||||
|
const needsMachineUpdate = hasMetadataChanges || hasHostnameChange || ...
|
||||||
|
|
||||||
|
// So atualizar machines se houver mudancas reais
|
||||||
|
if (needsMachineUpdate) {
|
||||||
|
await ctx.db.patch(machine._id, { ...patch }) // SEM lastHeartbeatAt
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Filtragem de campos volumetricos**
|
||||||
|
|
||||||
|
`INVENTORY_BLOCKLIST` remove campos que mudam frequentemente ou sao muito grandes:
|
||||||
|
- `software` (lista de programas instalados)
|
||||||
|
- `extended` (dados estendidos)
|
||||||
|
|
||||||
|
Hardware (CPU, memoria, placa-mae) **permanece visivel** em `/admin/devices`.
|
||||||
|
|
||||||
|
### Resultado
|
||||||
|
|
||||||
|
| Metrica | Antes | Depois |
|
||||||
|
|---------|-------|--------|
|
||||||
|
| Uso de memoria | 8-10 GiB | 4-5 GiB |
|
||||||
|
| Versoes por maquina | ~50/dia | ~2-3/dia |
|
||||||
|
| Percentual RAM (20 GiB) | 40-50% | 20-25% |
|
||||||
|
|
||||||
|
### Monitoramento
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verificar uso de memoria
|
||||||
|
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}' | grep convex
|
||||||
|
|
||||||
|
# Verificar OOM kills recentes
|
||||||
|
journalctl -k | grep -i 'oom\|killed' | tail -20
|
||||||
|
|
||||||
|
# Restart do servico se necessario
|
||||||
|
docker service update --force sistema_convex_backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Limites de Memoria (Docker Swarm)
|
||||||
|
|
||||||
|
Configurado em `stack.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
convex_backend:
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 20G
|
||||||
|
reservations:
|
||||||
|
memory: 8G
|
||||||
|
```
|
||||||
|
|
||||||
|
Para alterar via CLI:
|
||||||
|
```bash
|
||||||
|
docker service update --limit-memory 20G --reserve-memory 8G sistema_convex_backend
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11) Convex Self-Hosted — Erros de shape_inference (WebSocket 1006)
|
||||||
|
|
||||||
|
### Problema
|
||||||
|
|
||||||
|
O Convex self-hosted usa um sistema de **shape inference** para otimizar queries e exports. Quando documentos da mesma tabela tem campos com tipos incompativeis, o sistema falha ao unificar os "shapes".
|
||||||
|
|
||||||
|
**Sintoma observado:**
|
||||||
|
- WebSocket desconectando com codigo `1006` (abnormal closure)
|
||||||
|
- Logs com erro `shape_inference` repetidos
|
||||||
|
- Export em loop: `Export <id> beginning...` repetindo indefinidamente
|
||||||
|
|
||||||
|
**Causa raiz:**
|
||||||
|
A tabela `_scheduled_job_logs` acumulou milhares de registros de cron jobs. Alguns tinham:
|
||||||
|
- `logLines: []` (array vazio)
|
||||||
|
- `logLines: ["mensagem de log"]` (array com strings)
|
||||||
|
|
||||||
|
O shape inference nao consegue unificar `Array<never>` com `Array<string>`, causando falha continua.
|
||||||
|
|
||||||
|
### Solucao Implementada (2025-12-09)
|
||||||
|
|
||||||
|
**1. Garantir que cron jobs sempre produzam logs**
|
||||||
|
|
||||||
|
Adicionamos `console.log()` obrigatorio no inicio de cada handler de cron:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// convex/liveChat.ts - autoEndInactiveSessions
|
||||||
|
handler: async (ctx) => {
|
||||||
|
console.log("cron: autoEndInactiveSessions iniciado")
|
||||||
|
// ... resto do codigo
|
||||||
|
}
|
||||||
|
|
||||||
|
// convex/usbPolicy.ts - cleanupStalePendingPolicies
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
console.log("cron: cleanupStalePendingPolicies iniciado")
|
||||||
|
// ... resto do codigo
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Por que funciona**
|
||||||
|
|
||||||
|
Com o log obrigatorio, **todos** os registros de `_scheduled_job_logs` terao:
|
||||||
|
- `logLines: ["cron: <nome> iniciado", ...]`
|
||||||
|
|
||||||
|
Isso garante consistencia de tipos (sempre `Array<string>`), evitando o conflito de shapes.
|
||||||
|
|
||||||
|
### Arquivos Modificados
|
||||||
|
|
||||||
|
- `convex/liveChat.ts:751` — log no inicio de `autoEndInactiveSessions`
|
||||||
|
- `convex/usbPolicy.ts:313` — log no inicio de `cleanupStalePendingPolicies`
|
||||||
|
|
||||||
|
### Monitoramento
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verificar se ha erros de shape_inference nos logs
|
||||||
|
ssh root@154.12.253.40 "docker service logs sistema_convex_backend 2>&1 | grep -i 'shape_inference' | tail -10"
|
||||||
|
|
||||||
|
# Verificar status dos cron jobs no dashboard
|
||||||
|
# Acesse: convex-admin.esdrasrenan.com.br > Scheduled Jobs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notas
|
||||||
|
|
||||||
|
- Este e um bug interno do Convex self-hosted que pode ser corrigido em versoes futuras
|
||||||
|
- A solucao de adicionar logs obrigatorios e um workaround que nao afeta performance
|
||||||
|
- Se novos cron jobs forem adicionados, **sempre incluir um console.log no inicio**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue