Configuração do Banco de Dados
PostgreSQL, migrações e otimizações
O Sinapse utiliza PostgreSQL como banco de dados principal, com suporte a operações assíncronas via SQLAlchemy.
PostgreSQL
Requisitos Mínimos
- Versão: PostgreSQL 15 ou superior
- Memória: 4GB RAM (desenvolvimento), 16GB+ (produção)
- Armazenamento: SSD recomendado
- Conexões: 100+ conexões simultâneas
String de Conexão
# Formato da URL de conexão
DATABASE_URL=postgresql+asyncpg://usuario:senha@host:porta/banco
# Exemplo desenvolvimento
DATABASE_URL=postgresql+asyncpg://sinapse_user:sinapse123@localhost:5432/sinapse_db
# Exemplo produção
DATABASE_URL=postgresql+asyncpg://sinapse_user:${DB_PASSWORD}@db.sinapse.internal:5432/sinapse_prodUse sempre postgresql+asyncpg:// para operações assíncronas. O driver asyncpg é necessário para o funcionamento correto do sistema.
Configurações do PostgreSQL
postgresql.conf Otimizado
# Memória
shared_buffers = 4GB # 25% da RAM total
effective_cache_size = 12GB # 75% da RAM total
work_mem = 64MB # Por operação de sort
maintenance_work_mem = 1GB # Para VACUUM, CREATE INDEX
# Conexões
max_connections = 200
max_prepared_transactions = 100
# Write Ahead Log
wal_level = replica
max_wal_size = 2GB
min_wal_size = 512MB
# Query Planning
random_page_cost = 1.1 # Para SSD
effective_io_concurrency = 200 # Para SSD
# Logging
log_statement = 'mod' # Log INSERT, UPDATE, DELETE
log_duration = on
log_min_duration_statement = 100 # Log queries > 100msMigrações com Alembic
Gerenciador de Migrações
O Sinapse inclui um script elegante para gerenciar migrações de forma padronizada:
# Verificar se há mudanças no schema
python scripts/manage_migrations.py check
# Criar nova migração automaticamente
python scripts/manage_migrations.py create "descrição da mudança"
# Aplicar migrações
python scripts/manage_migrations.py upgrade
# Reverter última migração (com confirmação)
python scripts/manage_migrations.py downgrade
# Ver histórico de migrações
python scripts/manage_migrations.py history
# Ver versão atual do banco
python scripts/manage_migrations.py currentO script manage_migrations.py automatiza tarefas comuns e adiciona validações de segurança, como confirmação antes de reverter migrações.
Estrutura de Migrações
migrations/
├── versions/
│ ├── 001_initial_schema.py
│ ├── 002_add_usuarios.py
│ └── 003_add_agravos.py
├── alembic.ini
├── env.py
└── script.py.makoFluxo de Trabalho Recomendado
-
Antes de fazer mudanças, verifique o estado:
python scripts/manage_migrations.py check -
Após alterar models, crie a migração:
python scripts/manage_migrations.py create "adicionar campo telefone" -
Revise o arquivo gerado em
migrations/versions/ -
Aplique a migração:
python scripts/manage_migrations.py upgrade
Comandos Alembic Diretos
Para casos avançados, você ainda pode usar o Alembic diretamente:
# Criar migração manual (sem auto-generate)
alembic revision -m "migração customizada"
# Aplicar até versão específica
alembic upgrade abc123
# Reverter tudo
alembic downgrade base
# Ver SQL sem executar
alembic upgrade head --sqlRecursos do Script de Gerenciamento
O manage_migrations.py oferece várias vantagens:
-
Detecção Automática de Mudanças:
- Compara models com schema do banco
- Alerta sobre mudanças pendentes
- Evita esquecimento de migrações
-
Interface Amigável:
- Mensagens coloridas e emojis informativos
- Confirmação antes de ações destrutivas
- Descrições claras do que está acontecendo
-
Compatibilidade Async:
- Remove automaticamente
+asyncpgda URL - Funciona com configurações assíncronas
- Remove automaticamente
-
Validações de Segurança:
- Confirma antes de downgrade
- Verifica integridade das migrações
- Mostra preview antes de aplicar
Exemplo de Migração
"""adicionar campo telefone ao usuario
Revision ID: abc123
Revises: def456
Create Date: 2025-07-29 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers
revision = 'abc123'
down_revision = 'def456'
def upgrade():
op.add_column('usuarios',
sa.Column('telefone', sa.String(20), nullable=True)
)
op.create_index('idx_usuarios_telefone', 'usuarios', ['telefone'])
def downgrade():
op.drop_index('idx_usuarios_telefone', 'usuarios')
op.drop_column('usuarios', 'telefone')Boas Práticas para Migrações
-
Sempre revisar antes de aplicar:
# Após criar, revise o arquivo gerado cat migrations/versions/$(ls -t migrations/versions | head -1) -
Testar rollback em desenvolvimento:
# Aplicar python scripts/manage_migrations.py upgrade # Testar rollback python scripts/manage_migrations.py downgrade # Reaplicar python scripts/manage_migrations.py upgrade -
Backup antes de migrações em produção:
# Fazer backup pg_dump sinapse_db > backup_antes_migracao.sql # Aplicar migração python scripts/manage_migrations.py upgrade
Pool de Conexões
Configuração SQLAlchemy
# core/database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.pool import NullPool, QueuePool
# Desenvolvimento
engine = create_async_engine(
DATABASE_URL,
poolclass=NullPool, # Sem pool para hot reload
echo=True # Log de queries
)
# Produção
engine = create_async_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=20, # Conexões no pool
max_overflow=40, # Conexões extras
pool_timeout=30, # Timeout para obter conexão
pool_recycle=1800, # Reciclar conexões após 30min
echo=False
)Variáveis de Ambiente
# Pool de Conexões
DB_POOL_SIZE=20
DB_POOL_MAX_OVERFLOW=40
DB_POOL_TIMEOUT=30
DB_POOL_RECYCLE=1800
# Configurações Adicionais
DB_ECHO=false # Log de queries
DB_CONNECT_TIMEOUT=10 # Timeout de conexão
DB_COMMAND_TIMEOUT=60 # Timeout de comandosÍndices e Performance
Índices Essenciais
-- Índices para autenticação
CREATE INDEX idx_usuarios_email ON usuarios(email);
CREATE INDEX idx_usuarios_ativo ON usuarios(ativo) WHERE ativo = true;
-- Índices para permissões
CREATE INDEX idx_usuario_permissao_usuario ON usuario_permissao(usuario_id);
CREATE INDEX idx_usuario_grupo_usuario ON usuario_grupo(usuario_id);
-- Índices para agravos
CREATE INDEX idx_agravos_tipo_data ON agravos_casos(tipo_agravo, data_notificacao DESC);
CREATE INDEX idx_agravos_municipio ON agravos_casos(municipio_residencia);
-- Índices para busca textual
CREATE INDEX idx_usuarios_search ON usuarios USING gin(
to_tsvector('portuguese', nome || ' ' || email)
);Análise de Performance
-- Analisar query plan
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM usuarios WHERE email = '[email protected]';
-- Estatísticas de tabelas
SELECT
schemaname,
tablename,
n_live_tup as linhas,
n_dead_tup as linhas_mortas,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
-- Queries lentas
SELECT
query,
calls,
total_time,
mean_time,
max_time
FROM pg_stat_statements
WHERE mean_time > 100
ORDER BY mean_time DESC
LIMIT 10;Backup e Recuperação
Backup Automático
#!/bin/bash
# backup_sinapse.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/postgres"
DB_NAME="sinapse_db"
# Backup completo
pg_dump -h localhost -U sinapse_user -d $DB_NAME \
--format=custom \
--verbose \
--file="$BACKUP_DIR/sinapse_$DATE.dump"
# Comprimir backups antigos
find $BACKUP_DIR -name "*.dump" -mtime +7 -exec gzip {} \;
# Remover backups com mais de 30 dias
find $BACKUP_DIR -name "*.gz" -mtime +30 -deleteRestore
# Restore de backup
pg_restore -h localhost -U sinapse_user -d sinapse_db \
--verbose \
--clean \
--if-exists \
sinapse_20250729_120000.dumpMonitoramento
Queries para Monitoramento
-- Conexões ativas
SELECT
pid,
usename,
application_name,
client_addr,
state,
query_start,
state_change
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start;
-- Tamanho das tabelas
SELECT
schemaname || '.' || tablename AS tabela,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS tamanho
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- Bloat de índices
SELECT
schemaname || '.' || tablename AS tabela,
indexname AS indice,
pg_size_pretty(pg_relation_size(indexrelid)) AS tamanho,
idx_scan as scans
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC;Manutenção
VACUUM e ANALYZE
-- VACUUM manual
VACUUM (VERBOSE, ANALYZE) usuarios;
-- Reindex
REINDEX TABLE usuarios;
-- Atualizar estatísticas
ANALYZE usuarios;Configuração de Autovacuum
# postgresql.conf
autovacuum = on
autovacuum_max_workers = 4
autovacuum_naptime = 30s
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
autovacuum_vacuum_scale_factor = 0.1
autovacuum_analyze_scale_factor = 0.05Segurança
Configurações de Segurança
-- Criar usuário com permissões limitadas
CREATE USER sinapse_app WITH PASSWORD 'senha_segura';
GRANT CONNECT ON DATABASE sinapse_db TO sinapse_app;
GRANT USAGE ON SCHEMA public TO sinapse_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO sinapse_app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO sinapse_app;
-- Revogar permissões desnecessárias
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON DATABASE sinapse_db FROM PUBLIC;SSL/TLS
# Conexão com SSL
DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/db?ssl=require
# Certificados
DB_SSL_CERT=/path/to/client-cert.pem
DB_SSL_KEY=/path/to/client-key.pem
DB_SSL_CA=/path/to/ca-cert.pemTroubleshooting
Problemas Comuns
Erro: connection refused
# Verificar se PostgreSQL está rodando
sudo systemctl status postgresql
# Verificar configuração de rede
grep listen_addresses /etc/postgresql/15/main/postgresql.conf
# Verificar pg_hba.conf
cat /etc/postgresql/15/main/pg_hba.confQueries lentas
-- Identificar queries problemáticas
SELECT query, calls, mean_time
FROM pg_stat_statements
WHERE mean_time > 1000
ORDER BY mean_time DESC;
-- Verificar índices faltando
SELECT schemaname, tablename, attname, n_distinct, correlation
FROM pg_stats
WHERE schemaname = 'public'
AND n_distinct > 100
AND correlation < 0.1
ORDER BY n_distinct DESC;Deadlocks e locks
-- Ver locks ativos
SELECT
pid,
usename,
pg_blocking_pids(pid) as blocked_by,
query
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;
-- Matar processo bloqueado
SELECT pg_terminate_backend(pid);Espaço em disco
-- Verificar espaço usado
SELECT
pg_database_size('sinapse_db') as tamanho_bytes,
pg_size_pretty(pg_database_size('sinapse_db')) as tamanho_formatado;
-- VACUUM FULL para recuperar espaço
VACUUM FULL usuarios;