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:
esdrasrenan 2025-12-10 00:01:49 -03:00
parent 48d9de8dd1
commit e2dde8510a
4 changed files with 162 additions and 1 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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/email 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/email 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**