De 3 Minutos para 500ms: O Bug de Cadastro Que Não Fazia Sentido
Fui atrás de um atraso de 3 minutos no cadastro que acabou sendo o usuário de Schrödinger — existindo e não existindo ao mesmo tempo por causa do lag de replicação do banco de dados entre escritas e leituras.
Você conhece aquela sensação quando usuários reportam um bug que não faz nenhum sentido? "O app leva 3 minutos para carregar depois que eu me cadastro." Três minutos? Isso não é tempo de carregamento, é uma pausa para o café. Esta é a história de como rastreei um dos bugs mais estranhos na história do DIALØGUE.
O Mistério Começa
Começou de forma inocente. Um novo usuário se cadastrou usando Google SSO, animado para experimentar o DIALØGUE. Depois… nada. Bem, não exatamente nada — eles receberam nosso skeleton de carregamento lindamente desenhado. Por três minutos inteiros.
A parte estranha? Só acontecia com usuários novos. Usuários existentes conseguiam fazer login instantaneamente. E não era consistente — às vezes era 1 minuto, às vezes 3, ocasionalmente funcionava imediatamente.
Meu primeiro pensamento: "Deve ser um problema de cold start." (Narrador: Não era um problema de cold start.)
A Investigação
Rodada 1: Culpar o Frontend
```typescript
// Primeiro suspeito: O hook de carregamento de perfil
useEffect(() => \{
if (user) {
fetchUserProfile(); // Isso estava demorando uma eternidade
\}
}, [user]);
```
Adicionei timers em todo lugar. A chamada de API de fato estava levando 3 minutos. Mas por quê? O backend deveria ou retornar dados ou dar erro, não simplesmente… esperar.
Rodada 2: Culpar o Backend
Mergulhei nas nossas Edge Functions do Supabase:
```typescript
// Edge Function para obter perfil do usuário
const \{ data: profile \} = await supabase
.from('users')
.select('*')
.eq('id', userId)
.single();
if (!profile) \{
// Novo usuário - crie o perfil
await createUserProfile(userId);
\}
```
Isso parecia correto. Deveria ser rápido, certo? Hora de adicionar mais logging.
Rodada 3: A Trama Engrossa
Depois de adicionar logs em todo lugar (e quero dizer todo lugar), descobri algo bizarro:
[00:00] Usuário faz login com Google
[00:01] Auth trigger dispara - cria registro do usuário
[00:01] Frontend solicita perfil
[00:01] Edge Function consulta o usuário... SEM RESULTADO
[00:02] Edge Function tenta criar usuário...
[00:02] Erro de restrição no banco: Usuário já existe
[00:03] Função tenta novamente...
[03:00] Função finalmente dá timeout
Espera, o quê? O usuário não existe, mas já existe? O usuário de Schrödinger? T.T
A Revelação
Depois de olhar para logs de banco de dados até os olhos doerem, finalmente vi. Nosso banco de dados tinha processos concorrentes:
- Auth Trigger do Supabase: Cria registro do usuário no cadastro
- Edge Function: Tenta criar usuário se não encontrado
- Lag de Replicação do Banco: O INSERT do trigger ainda não tinha replicado para a réplica de leitura
Aqui está o que estava acontecendo:
-- Auth trigger (no banco primário)
INSERT INTO users (id, email) VALUES ($1, $2);
-- Edge Function (lendo da réplica)
SELECT * FROM users WHERE id = $1; -- Não retorna nada!
-- Edge Function (tentando ajudar)
INSERT INTO users (id, email) VALUES ($1, $2); -- CONFLITO!
A função tentaria novamente com backoff exponencial, cada tentativa batendo na mesma race condition até que:
- A replicação finalmente alcançasse (1-3 minutos)
- A função desse timeout (3 minutos)
Atualização: O Culpado Real e a Solução Final Robusta
Depois do post inicial, continuamos debugando, e embora nossas mudanças no backend fossem melhorias, a raiz do mistério permanecia esquiva. A virada veio quando mudamos nosso foco do backend para a hidratação client-side e o fluxo de autenticação.
O Culpado Real: Uma Race Condition Client-Side
O problema não era um trigger de banco de dados lento ou uma Edge Function fria. O verdadeiro problema era uma race condition clássica do lado do cliente:
- Redirecionamento OAuth: Um novo usuário faz login com Google e é redirecionado de volta para nosso app.
- Sessão Assíncrona: A biblioteca cliente Supabase (
supabase-js) começa a processar o token da URL para estabelecer uma sessão. Este é um processo assíncrono. - Render Prematuro: Nosso app React, entretanto, renderiza imediatamente. Ele solicita a sessão do usuário antes que o processo assíncrono do passo 2 esteja completo.
- A Falha: O app recebe uma sessão
null, conclui que o usuário não está logado e renderiza um estado em branco ou de erro. Alguns momentos depois, a sessão se torna disponível, mas já é tarde demais — a UI já tomou sua decisão.
Nossas soluções iniciais, como adicionar retries do lado do cliente, eram apenas sintomas de lutar contra essa race condition fundamental.
A Solução: Uma Única Fonte de Verdade para Autenticação
A solução correta e final foi rearquitetar nosso gerenciamento de estado de autenticação no frontend para ser verdadeiramente orientado a eventos e robusto.
1. Lógica Centralizada no SupabaseProvider:
Refatoramos nosso SupabaseProvider para ser a única fonte autoritativa de verdade para autenticação. Removemos todos os outros listeners e verificações de outros hooks.
2. Usando onAuthStateChange Corretamente:
O núcleo da correção foi depender exclusivamente do listener onAuthStateChange do Supabase.
// Lógica simplificada no SupabaseProvider.tsx
export function SupabaseProvider(\{ children \}) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true); // Começa em estado de carregamento
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => \{
setUser(session?.user ?? null);
// Só consideramos a autenticação "finalizada" quando este listener dispara.
setLoading(false);
\}
);
return () => subscription.unsubscribe();
}, []);
// ...
}
Esse padrão garante que toda a aplicação permanece em estado de loading até que o Supabase confirme que a sessão do usuário é válida ou null. Não há mais race condition.
O Final Feliz
O DIALØGUE agora integra novos usuários em menos de um segundo. Sem mais pausas para o café durante o cadastro. Sem mais usuários confusos se perguntando se quebraram algo.
A correção está em produção há 3 semanas. Zero problemas de timeout. Zero race conditions. Apenas cadastros suaves e rápidos como deveria ter sido desde o começo.
Valeu a pena passar uma semana debugando isso? Quando vejo novos usuários criar seu primeiro podcast em minutos após o cadastro — absolutamente. :D
Você já rastreou um bug onde a coisa simultaneamente existia e não existia? Sinto que todo desenvolvedor tem pelo menos uma história de bug de Schrödinger. Adoraria ouvir a sua!
Abraços,
Chandler
Quer experimentar o cadastro agora ultra-rápido? Crie seu podcast de IA no DIALØGUE. Prometo que não vai demorar 3 minutos mais :)





