# QueueMaster - Troubleshooting

## Índice

1. [Workers não pegam jobs](#workers-não-pegam-jobs)
2. [Workers morrem inesperadamente](#workers-morrem-inesperadamente)
3. [Problemas de Memória](#problemas-de-memória)
4. [Problemas de VHost](#problemas-de-vhost)
5. [Problemas de Tenant Switching](#problemas-de-tenant-switching)
6. [Workers Zumbis/Órfãos](#workers-zumbisórfãos)
7. [Como Debugar](#como-debugar)
8. [Master não inicia](#master-não-inicia)
9. [Autoscaling não funciona](#autoscaling-não-funciona)
10. [Jobs travando](#jobs-travando)

---

## Workers não pegam jobs

### Sintoma

Workers estão rodando mas não processam jobs da fila, mesmo com mensagens pendentes.

### Diagnóstico

#### 1. Verificar se workers estão ativos

```bash
ps aux | grep "queue:work" | grep -v grep
```

Se não retornar nada: workers não estão rodando.

#### 2. Verificar se fila tem mensagens

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/queues | \
  jq '.data.queues[] | select(.name == "default") | {name, messages, consumers}'
```

**Output esperado**:
```json
{
  "name": "default",
  "messages": 42,
  "consumers": 5
}
```

Se `messages = 0`: Não há jobs na fila.

#### 3. Verificar VHost

```bash
ps aux | grep "queue:work" | grep -v grep | head -1
```

Procure por `--vhost=/tenant1` no comando.

```bash
# Verificar se job foi enviado para o mesmo vhost
# Ver RabbitMQ Management
curl -u guest:guest http://localhost:15672/api/queues/%2Ftenant1/default
```

#### 4. Verificar logs de workers

```bash
tail -n 100 docker/logs/worker/error.log | grep -i error
```

### Causas Comuns

#### Causa 1: VHost Incompatível

**Problema**: Worker escutando `/tenant1`, jobs enviados para `/tenant2`

**Solução**:

```bash
# Verificar vhost do worker
ps aux | grep "queue:work" | grep vhost

# Enviar job para vhost correto
QueueManager::connection('rabbitmq', ['vhost' => '/tenant1'])
    ->push(new SendEmailJob());
```

#### Causa 2: Nome de Fila Errado

**Problema**: Worker escutando `default`, job enviado para `emails`

**Solução**:

```php
// No job
public function queue(): string
{
    return 'default';  // Deve corresponder à fila do worker
}
```

#### Causa 3: RabbitMQ Connection Issue

**Problema**: Worker não consegue conectar ao RabbitMQ

**Diagnóstico**:

```bash
# Testar conexão manualmente
php og queue:work default --once --vhost=/tenant1
```

Se falhar, verificar:

```bash
# RabbitMQ está rodando?
systemctl status rabbitmq-server

# Credenciais corretas?
cat Modules/Common/Config/database.php | grep -A 10 rabbitmq
```

**Solução**:

```php
// Verificar config
'rabbitmq' => [
    'host' => envVar('RABBITMQ_HOST', 'localhost'),
    'port' => envVar('RABBITMQ_PORT', 5672),
    'user' => envVar('RABBITMQ_USER', 'guest'),
    'password' => envVar('RABBITMQ_PASSWORD', 'guest'),
    'vhost' => envVar('RABBITMQ_VHOST', '/'),
],
```

#### Causa 4: Workers em Estado de Erro

**Diagnóstico**:

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/workers | \
  jq '.data.workers[] | select(.status == "error")'
```

**Solução**: Restart do QueueMaster

```bash
kill -SIGTERM <master_pid>
php og queue-master &
```

---

## Workers morrem inesperadamente

### Sintoma

Workers desaparecem do sistema sem causa aparente.

### Diagnóstico

#### 1. Verificar Shutdown History

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/shutdowns/history?limit=20 | \
  jq '.data.shutdowns[] | {pid, shutdown_reason, uptime_seconds, jobs_processed}'
```

#### 2. Analisar Razões

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/shutdowns/stats | \
  jq '.data.by_reason'
```

**Output**:
```json
{
  "max_time_reached": 500,
  "memory_limit": 200,
  "fatal_error": 50,
  "signal_sigterm": 10
}
```

### Causas por Shutdown Reason

#### `memory_limit`

**Problema**: Workers excedem limite de memória

**Diagnóstico**:

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/workers | \
  jq '.data.workers[] | {pid, memory_usage_mb, memory_limit_mb: (.memory_limit / 1024 / 1024)}'
```

**Soluções**:

1. **Aumentar limite**:
```php
'memory' => 1024,  // Era 512
```

2. **Otimizar jobs** (liberar memória):
```php
public function handle()
{
    $largeData = $this->processLargeDataset();

    // Processar...

    // Liberar memória explicitamente
    unset($largeData);
    gc_collect_cycles();
}
```

3. **Reduzir maxJobs** (recicla mais cedo):
```php
'maxJobs' => 50,  // Era 0
```

#### `max_time_reached`

**Problema**: Worker atinge `workerMaxTime`

**Isso é NORMAL** se configurado intencionalmente.

**Se não esperado**:

```php
// Aumentar limite
'workerMaxTime' => 600,  // Era 300
```

#### `fatal_error`

**Problema**: Bug no código do job

**Diagnóstico**:

```bash
# Ver failed jobs
curl -s https://og-tenant1.test:8003/api/queue-master/jobs/failed?limit=20 | \
  jq '.data.jobs[] | {job_class, exception_message, trace_snippet}'

# Ver logs
tail -n 500 docker/logs/worker/error.log | grep -A 20 "Fatal error"
```

**Solução**: Corrigir bug identificado no stack trace

#### `signal_sigterm` ou `signal_sigint`

**Problema**: Alguém/algo matou o worker

**Diagnóstico**:

```bash
# Verificar se foi o Master (shutdown gracioso)
tail -f docker/logs/worker/error.log | grep -i "sigterm\|sigint"

# Verificar OOM Killer (out of memory)
dmesg | grep -i "killed process"
journalctl | grep -i "out of memory"
```

**Se OOM Killer**:

Sistema está sem memória. Soluções:

1. Adicionar mais RAM ao servidor
2. Reduzir número de workers (`maxProcesses`)
3. Reduzir `memory` por worker

#### `unknown`

**Problema**: Worker morreu sem razão conhecida

**Diagnóstico**:

```bash
# Verificar kernel logs
dmesg | tail -100

# Verificar system logs
journalctl -xe | tail -100

# Verificar se processo foi morto
grep "killed" /var/log/syslog
```

**Causas possíveis**:
- OOM Killer
- Kernel panic
- Hardware failure
- Usuário matou processo manualmente

---

## Problemas de Memória

### Sintoma

Workers reciclam constantemente com `memory_limit`.

### Diagnóstico Detalhado

#### 1. Verificar padrão de uso de memória

```bash
# Memória atual de cada worker
curl -s https://og-tenant1.test:8003/api/queue-master/workers | \
  jq '.data.workers[] | {
    pid,
    memory_mb: .memory_usage_mb,
    limit_mb: (.memory_limit / 1024 / 1024),
    percent: ((.memory_usage / .memory_limit) * 100)
  }'
```

**Output**:
```json
{
  "pid": 12345,
  "memory_mb": 480.5,
  "limit_mb": 512.0,
  "percent": 93.8
}
```

Se `percent > 90%`: Worker reciclará em breve.

#### 2. Monitorar tendência

```bash
# A cada 5 segundos
watch -n 5 'curl -s https://og-tenant1.test:8003/api/queue-master/workers | \
  jq "[.data.workers[].memory_usage_mb] | add / length"'
```

Se memória CRESCE linearmente: Vazamento de memória!

### Soluções

#### Solução 1: Aumentar Limite

```php
'memory' => 1024,  // Dobrar de 512
```

#### Solução 2: Forçar Garbage Collection

```php
// No job
public function handle()
{
    // ... processar ...

    // Force GC
    gc_collect_cycles();
}

// No worker (após cada job - já implementado)
// Worker.php:420
gc_collect_cycles();
```

#### Solução 3: Reciclar Mais Cedo

```php
'maxJobs' => 20,  // Recicla após 20 jobs
```

#### Solução 4: Identificar e Corrigir Vazamento

```php
// Adicionar log de memória no job
public function handle()
{
    $memBefore = memory_get_usage(true) / 1024 / 1024;

    // Processar...

    $memAfter = memory_get_usage(true) / 1024 / 1024;

    loggerBatch('info', 'Memory usage', [
        'job' => static::class,
        'before' => $memBefore,
        'after' => $memAfter,
        'diff' => $memAfter - $memBefore,
    ]);
}
```

Identificar qual job consome mais memória e otimizar.

---

## Problemas de VHost

### Sintoma

Jobs não são processados, mas apenas para um tenant específico.

### Diagnóstico

#### 1. Verificar VHost do Worker

```bash
ps aux | grep "queue:work" | grep -v grep
```

Procure por `--vhost=/tenant1`.

#### 2. Verificar VHost das Filas

```bash
# RabbitMQ Management API
curl -u guest:guest http://localhost:15672/api/queues | \
  jq '.[] | {name, vhost}'
```

#### 3. Verificar Isolamento

```bash
# Listar workers por vhost
curl -s https://og-tenant1.test:8003/api/queue-master/workers | \
  jq '.data.workers | group_by(.vhost)[] | {
    vhost: .[0].vhost,
    count: length
  }'
```

### Soluções

#### Problema: VHost não existe no RabbitMQ

```bash
# Criar vhost
rabbitmqctl add_vhost /tenant1

# Dar permissões
rabbitmqctl set_permissions -p /tenant1 guest ".*" ".*" ".*"
```

#### Problema: Worker no VHost errado

```bash
# Parar worker atual
kill -SIGTERM <worker_pid>

# Iniciar com vhost correto
php og queue-master --vhost=/tenant1
```

#### Problema: Job enviado para VHost errado

```php
// Corrigir dispatch do job
QueueManager::connection('rabbitmq', ['vhost' => '/tenant1'])
    ->push(new SendEmailJob());

// Ou no job
public function vhost(): string
{
    return '/tenant1';
}
```

---

## Problemas de Tenant Switching

### Sintoma

Jobs falham com erros relacionados a tenant/contexto errado.

### Diagnóstico

#### 1. Verificar Tenant Switches Problemáticos

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/tenant-switches/problematic | \
  jq '.data.switches[]'
```

#### 2. Verificar Taxa de Falha

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/tenant-switches/stats | \
  jq '{
    total: .data.total_switches,
    failed: .data.failed_switches,
    failure_rate: .data.failure_rate
  }'
```

Se `failure_rate > 5%`: Problema sério!

### Causas Comuns

#### Causa 1: Job não implementa `domain()`

**Problema**:

```php
class MyJob extends Job
{
    public function domain(): ?string
    {
        return null;  // PROBLEMA!
    }
}
```

**Solução**:

```php
class MyJob extends Job
{
    private string $tenant;

    public function __construct(string $tenant)
    {
        $this->tenant = $tenant;
    }

    public function domain(): ?string
    {
        return $this->tenant;
    }
}
```

#### Causa 2: Tenant não serializa corretamente

**Problema**: Propriedade `$tenant` não é serializada

**Solução**:

```php
class MyJob extends Job
{
    use SerializesModels;  // Garante serialização correta

    private string $tenant;

    public function __construct(string $tenant)
    {
        $this->tenant = $tenant;
    }
}
```

#### Causa 3: Context não persiste

**Diagnóstico**:

```bash
# Ver detalhes de um switch problemático
curl -s https://og-tenant1.test:8003/api/queue-master/tenant-switches/problematic | \
  jq '.data.switches[0] | {from_tenant, to_tenant, error, isolation_issues}'
```

**Solução**: Verificar implementação de tenant context no job.

---

## Workers Zumbis/Órfãos

### Sintoma

Workers aparecem no Redis mas processo não está rodando, ou vice-versa.

### Diagnóstico

#### 1. Comparar Redis vs Sistema

```bash
# Workers no Redis
REDIS_COUNT=$(redis-cli KEYS "queuemaster:workers:*" | wc -l)

# Workers no sistema
SYSTEM_COUNT=$(ps aux | grep "queue:work" | grep -v grep | wc -l)

echo "Redis: $REDIS_COUNT"
echo "System: $SYSTEM_COUNT"
```

Se `REDIS_COUNT > SYSTEM_COUNT`: Órfãos no Redis

Se `REDIS_COUNT < SYSTEM_COUNT`: Processos não registrados

#### 2. Identificar Órfãos

```bash
redis-cli KEYS "queuemaster:workers:*" | while read key; do
    pid=$(echo $key | grep -oP '\d+$')
    if ! ps -p $pid > /dev/null 2>&1; then
        echo "Orphan: $key (PID $pid not running)"
    fi
done
```

### Soluções

#### Solução 1: Limpeza Automática

O Master limpa órfãos a cada 60s automaticamente.

Forçar limpeza:

```bash
# Restart do Master força cleanup
kill -SIGUSR2 <master_pid>
```

#### Solução 2: Limpeza Manual

```bash
# Remover órfãos do Redis
redis-cli KEYS "queuemaster:workers:*" | while read key; do
    pid=$(echo $key | grep -oP '\d+$')
    if ! ps -p $pid > /dev/null 2>&1; then
        redis-cli DEL "$key"
        echo "Removed: $key"
    fi
done
```

#### Solução 3: Kill Processos Órfãos

```bash
# Workers sem Master
ps aux | grep "queue:work" | grep -v grep | while read line; do
    pid=$(echo $line | awk '{print $2}')

    # Verificar se tem Master rodando
    if ! ps aux | grep "queue-master" | grep -v "queue:work" | grep -v grep > /dev/null; then
        echo "Killing orphan worker: $pid"
        kill -9 $pid
    fi
done
```

---

## Como Debugar

### Ativar Modo Debug

#### No Worker

```php
// Worker options
'debug' => true,
```

Ou variável de ambiente:

```bash
QUEUE_WORKER_DEBUG=true php og queue-master
```

**Output adicional**:
- Cada job processado
- Tenant switches detalhados
- Memory usage após cada job
- Heartbeats
- Verificações de limites

### Logs Estruturados

#### Ver Logs de um Worker Específico

```bash
tail -f docker/logs/worker/error.log | grep "PID=12345"
```

#### Ver Logs de uma Fila

```bash
tail -f docker/logs/worker/error.log | grep "queue=reports"
```

#### Ver Logs de Shutdown

```bash
tail -f docker/logs/worker/error.log | grep -i "stopping\|shutdown"
```

### Rastrear Job Específico

```bash
# 1. Pegar job_id do dashboard ou failed jobs
JOB_ID="550e8400-e29b-41d4-a716-446655440000"

# 2. Buscar em logs
grep -r "$JOB_ID" docker/logs/
grep -r "$JOB_ID" _logs/

# 3. Verificar no Redis
redis-cli GET "queuemaster:jobs:active:$JOB_ID"
```

### Executar Job Manualmente

```bash
# Testar job fora do QueueMaster
php og queue:work default --once --vhost=/tenant1

# Com debug
php og queue:work default --once --vhost=/tenant1 --debug
```

### Verificar Stack Completo

```php
// No código do job, adicionar debug
public function handle()
{
    try {
        // ... código ...
    } catch (\Throwable $e) {
        // Log completo
        loggerBatch('error', 'Job failed with exception', [
            'job' => static::class,
            'exception' => get_class($e),
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString(),
            'context' => [
                'tenant' => $this->domain(),
                'company' => $this->companyId(),
            ],
        ]);

        throw $e;
    }
}
```

---

## Master não inicia

### Sintoma

Comando `php og queue-master` não inicia ou retorna erro.

### Diagnóstico

```bash
# Tentar iniciar com output
php og queue-master

# Se travar, verificar logs
tail -f docker/logs/php/error.log
```

### Causas Comuns

#### Causa 1: Master Já Está Rodando

**Erro**: `A master supervisor is already running on this machine.`

**Solução**:

```bash
# Parar master existente
MASTER_PID=$(ps aux | grep "queue-master" | grep -v "queue:work" | grep -v grep | awk '{print $2}')
kill -SIGTERM $MASTER_PID

# Aguardar
sleep 5

# Tentar novamente
php og queue-master
```

#### Causa 2: Redis Não Conecta

**Erro**: `Connection refused [tcp://127.0.0.1:6379]`

**Solução**:

```bash
# Redis está rodando?
systemctl status redis

# Testar conexão
redis-cli ping
# Esperado: PONG
```

#### Causa 3: Extensão PCNTL Faltando

**Erro**: `Call to undefined function pcntl_signal`

**Solução**:

```bash
# Verificar se extensão está instalada
php -m | grep pcntl

# Se não, instalar
sudo apt install php8.3-pcntl
sudo systemctl restart php8.3-fpm
```

#### Causa 4: Configuração Inválida

**Erro**: Parse error ou exception no config

**Solução**:

```bash
# Validar sintaxe do config
php -l Modules/Common/Config/queue-master.php

# Verificar valores
php -r "print_r(require 'Modules/Common/Config/queue-master.php');"
```

---

## Autoscaling não funciona

### Sintoma

Workers não aumentam/diminuem conforme carga, mesmo com `balance = 'auto'`.

### Diagnóstico

#### 1. Verificar Configuração

```php
// queue-master.php
'balance' => 'auto',  // Deve estar 'auto'

'autoscaling' => [
    'enabled' => true,  // Deve estar true
]
```

#### 2. Verificar Carga da Fila

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/queues | \
  jq '.data.queues[] | {name, messages, consumers, pressure}'
```

Se `pressure < scale_up_threshold`: Não vai escalar.

#### 3. Ver Logs de Autoscaling

```bash
tail -f docker/logs/worker/error.log | grep -i "scal"
```

### Soluções

#### Problema: Balance está OFF

```php
// Corrigir config
'balance' => 'auto',  // Era 'off'
```

#### Problema: Thresholds muito altos

```php
'autoscaling' => [
    'scale_up_threshold' => 2.0,  // Era 10.0 (muito alto)
    'scale_down_threshold' => 0.5,
]
```

#### Problema: Cooldown muito longo

```php
'autoscaling' => [
    'cooldown' => 3,  // Era 60 (reage muito devagar)
]
```

---

## Jobs travando

### Sintoma

Jobs em execução não terminam, workers ficam "stuck".

### Diagnóstico

#### 1. Ver Jobs Ativos Há Muito Tempo

```bash
curl -s https://og-tenant1.test:8003/api/queue-master/jobs/active | \
  jq '.data.jobs[] | select(.duration_ms > 300000) | {
    job_class,
    duration_seconds: (.duration_ms / 1000),
    worker,
    started_at_iso
  }'
```

Jobs com `duration > 300s` (5 min) podem estar travados.

#### 2. Identificar Worker

```bash
# PID do worker travado
WORKER_PID=12345

# Ver o que está fazendo
strace -p $WORKER_PID

# Ver stack trace
gdb -p $WORKER_PID -batch -ex "thread apply all bt"
```

### Soluções

#### Solução 1: Timeout está muito alto

```php
'timeout' => 60,  // Era 600 (10 min)
```

#### Solução 2: Job tem loop infinito

Corrigir código do job:

```php
public function handle()
{
    $maxIterations = 1000;
    $i = 0;

    while ($condition && $i < $maxIterations) {
        // Processar...
        $i++;
    }

    if ($i >= $maxIterations) {
        throw new \RuntimeException('Max iterations reached');
    }
}
```

#### Solução 3: Conexão externa travada

```php
public function handle()
{
    // Usar timeout em requisições externas
    $client = new GuzzleHttp\Client([
        'timeout' => 30,  // 30s timeout
        'connect_timeout' => 10,
    ]);

    try {
        $response = $client->get('https://api.example.com/slow');
    } catch (\GuzzleHttp\Exception\TransferException $e) {
        // Tratar timeout
        throw new \RuntimeException('External API timeout', 0, $e);
    }
}
```

#### Solução 4: Kill Worker Travado

```bash
# Force kill worker específico
kill -9 12345

# Master criará novo worker automaticamente
```

---

## Próximos Documentos

- **[MONITORING.md](MONITORING.md)** - Como monitorar para prevenir problemas
- **[DEVELOPMENT.md](DEVELOPMENT.md)** - Como desenvolver e testar
- **[CONFIGURATION.md](CONFIGURATION.md)** - Tuning de parâmetros