1 pontos por GN⁺ 2024-08-21 | 1 comentários | Compartilhar no WhatsApp
  • Por volta das 22h, no jumpcomedy.com, todas as chamadas HTTP POST do RTK Query começaram a falhar, quebrando funcionalidades do site; como tudo funcionava normalmente no ambiente local, rastrear a causa foi difícil
  • Enquanto as reclamações dos clientes se acumulavam, o operador precisou lidar sozinho com a falha, sem suporte de produção, SRE, engenheiro sênior ou gerente
  • O TypeError relacionado ao fetch no navegador não ofereceu uma pista direta, já que GET e DELETE funcionavam normalmente
  • Mesmo verificando Sentry, banco de dados de produção, Cloudflare, atualização do Chrome e rollback para versões antigas, nada mudava; ao inserir a api_key do PostHog, que estava vazia localmente, o problema foi reproduzido
  • Depois de remover o PostHog, as funcionalidades voltaram ao normal; mais tarde, o mesmo incidente foi confirmado em issues do GitHub do PostHog e do Redux Toolkit, revelando a influência de uma ferramenta externa

A pressão criada pela falha

  • A partir de cerca das 22h, no jumpcomedy.com, todas as chamadas HTTP POST baseadas em RTK Query passaram a falhar, e as principais funcionalidades deixaram de operar corretamente
  • Havia mudanças implantadas recentemente, mas nada que parecesse ser a causa; como o problema não se reproduzia no ambiente local, o rastreamento ficou ainda mais difícil
  • Foram pedidos ajuda nos Discords do NextJS e da Vercel, mas não houve resposta, e também não havia uma equipe de suporte de produção para assumir a resposta ao incidente
  • Os e-mails de clientes continuavam se acumulando
    • Perguntas dizendo que não conseguiam alterar o preço de um evento
    • Perguntas dizendo que não conseguiam remover um código promocional
  • Como pequenos clientes empresariais dependiam do serviço, o operador sentiu vergonha, tristeza, sensação de incompetência e síndrome do impostor

Processo de depuração e confirmação da causa

  • O erro do navegador era um TypeError indicando que o fetch tinha sido executado com um request object já usado, mas isso não apontava a causa real
  • Foram adicionados vários console.log() e breakpoints para verificar headers, comprimento do token da API, ordem das chamadas etc., mas nada levou a uma causa clara
  • Suspeitou-se de uma atualização do Chrome, mas o problema também se reproduzia no Firefox e no Edge, então não era um problema exclusivo do navegador
  • Mesmo voltando para versões antigas, a falha continuava
    • Falhava também na versão de um mês antes
    • Falhava também na versão de três meses antes
    • Falhava também na versão de um ano antes
  • Para reduzir as diferenças entre local e produção, vários candidatos foram verificados
    • Remover o Sentry em produção: sem mudança
    • Conectar o ambiente local ao banco de dados de produção: sem mudança
    • Desativar o Cloudflare: sem mudança
  • No ambiente local, para reduzir custos, a api_key do PostHog estava vazia; ao adicioná-la, o mesmo problema foi reproduzido
  • No commit seguinte, ao remover o PostHog, todas as funcionalidades passaram a operar normalmente
  • O mesmo problema foi confirmado depois também em issues do GitHub

1 comentários

 
GN⁺ 2024-08-21
Comentários do Hacker News
  • Depois de trabalhar por 1 ano como SRE em uma grande empresa global, consegui sair do modo de “pânico” de que o texto fala
    Do ponto de vista do negócio, todo problema parece o fim do mundo, e é fácil entrar em pânico nessa situação, mas na prática raramente é tão ruim assim e, mesmo quando é ruim, na maioria das vezes você sobrevive sem maiores danos
    Nessas situações, o ponto principal é parar por 5 a 10 minutos antes de tentar mexer em algo para corrigir e desenhar o cenário da forma mais clara possível. O medo atrapalha o julgamento racional, e sair apertando botões em pânico pode complicar ainda mais o problema. Meu truque é jogar água bem gelada no rosto e nas mãos para interromper esse circuito do medo
    Depois de passar por isso algumas vezes, você ganha confiança de que não é tão terrível quanto parece e de que já lidou com situações ruins antes, então percebe que consegue aguentar mesmo sem ter alguém a quem pedir ajuda

    • É importante lembrar que, quando alguma coisa “quebra”, a empresa pode entrar em polvorosa, mas não se abala nem um pouco com problemas que podem ser ainda mais importantes
      Software comprado que não faz nada por falha de alocação de pessoal ou de configuração, funcionários perdendo milhares de horas por ano com experiência de usuário ruim e requisitos sem sentido, capacidades que não fazem função nenhuma sendo deixadas de lado, reuniões inúteis desperdiçando tempo todos os dias, funcionalidades que existem só para cumprir exigências de auditoria, executivos desperdiçando o dinheiro da empresa continuamente — esse tipo de coisa
      Downtime não parece pior do que esses problemas, mas atrai muito mais atenção e pânico. Parece o contraste entre terrorismo e doença cardíaca. A empresa não se importa com o seu sono nem com a sua saúde mental e vai forçar o máximo possível. Não quer dizer que haja maldade, mas nisso ela age como um agressor: empurra mais na medida em que você recua
    • Os piores erros que vi em incidentes reais quase sempre vieram de reação exagerada
      Um dos meus lemas de programação é “sem magia negra”. Se você não entende por que funciona, então ainda não acabou
      Vejo resposta a incidentes do mesmo jeito. Se alguém não consegue explicar de forma consistente por que uma sugestão vai ter efeito, então acho que não deveria ser executada. Pode ser que um dia chegue a hora de simplesmente puxar o gatilho, mas olhando para trás, acho que nunca houve de fato um caso assim
      Foi bem chocante ver executivos de alto escalão, normalmente muito calmos, começarem a jogar correções aleatórias durante um incidente
    • Por outro lado, quem precisava mudar o preço de eventos no jumpcomedy.com às 2 da manhã, mais ou menos dentro de ±2 fusos horários, provavelmente ficou profundamente decepcionado. Alguns deles talvez até tenham morrido
      Imagine quão maior poderia ter sido o estrago se ninguém tivesse parado essa pessoa desenvolvedora solo para dizer que ela não deveria tentar popularizar fetch
    • Um dos VPs mais legais que conheci costumava dizer: “devagar é suave, e suave é rápido
      É verdade que o medo atrapalha o julgamento racional, e eu acrescentaria que o medo também é extremamente contagioso. Quando quem está na linha de frente vê líderes, gerentes e colegas entrando em pânico, muitas vezes entra em pânico junto. Felizmente, meu VP sempre mantinha a calma e priorizava clareza acima de ação
    • No fim das contas, não é você quem assume esse risco. A empresa não é sua, a empresa pode te cortar em qualquer momento arbitrário e de fato fará isso. Claro, exceto se a empresa for sua
  • Não sei se isso chega a ser um colapso mental, e pode até passar uma impressão errada para pessoas que realmente passam por um colapso por estresse relacionado à tecnologia
    No meu caso aconteceu exatamente uma vez, e foi uma crise de ansiedade. Tive muita sorte porque minha esposa estava ao meu lado, explicou a situação e me ajudou a entender o que eu estava passando. Ela já teve várias; para mim foi a primeira e, felizmente, a última
    Isso pode acontecer com qualquer pessoa, e não há nada de errado nisso por si só. É muito importante internalizar que isso não significa que você seja defeituoso ou fraco
    No meu caso, o que acabou fazendo parar foi Xanax, e como eu consegui dormir, acho que vale a pena ter por perto
    O que quero dizer é que pensamentos intrusivos são uma coisa, e estados realmente incontroláveis e paralisantes, como uma crise de ansiedade ou de pânico, são outra. Quando isso acontece, você não consegue trabalhar, e tudo bem

    • https://www.webmd.com/mental-health/signs-nervous-breakdown
      Nem todo colapso se manifesta na forma de crise de pânico ou de ansiedade. Pode se manifestar assim, mas não é a única forma. O estresse aparece de maneiras muito diferentes dependendo da pessoa e até do fator de estresse
      Como não dá para saber exatamente o que aquela pessoa viveu dentro da própria cabeça, é quase impossível “diagnosticar” de fora. Mesmo que não tenha sido uma crise de pânico completa, ainda assim parece ter havido uma paralisia funcional por algumas horas
    • É preciso cuidado com a ideia de “ter Xanax por perto
      Se você pesquisar online, parece que o Xanax pode causar dependência
      https://www.drugs.com/xanax.html
      Não parece ser o tipo de coisa para tomar com leviandade
    • Eu não deveria precisar tomar remédio para aguentar funcionalidades sempre lançadas de qualquer jeito, mudanças empurradas sem pensar e os alertas do PagerDuty às 3 da manhã que isso causa
      Há uma boa chance de vermos a grande leva de pessoas que entrou no setor de tecnologia em meados dos anos 2000 morrer de doenças relacionadas ao estresse
    • Uma das coisas que eu não esperava ao trabalhar em empresa de tecnologia enterprise era quantos colegas tomavam Xanax regularmente
      Como alguém que teve ansiedade severa a vida inteira, a própria ideia de uma pílula viciante que faz tudo isso desaparecer dá medo. Acho que eu acabaria dependente para sempre
    • Na verdade, fiquei decepcionado porque no fim era só uma história comum de debug de dependência. Já senti algumas vezes que estava à beira de um colapso, então esperava um texto com o qual eu me identificasse mais
  • O estresse dessa pessoa foi causado por uma linha de código do PostHog. O commit revertido é este: https://github.com/PostHog/posthog-js/pull/1371/commits/7598...
    Dá para tirar duas lições daqui. Primeiro: se você fez deploy, então isso é seu. Portanto, quanto menos deploy você fizer, melhor, e dependências devem ser mantidas no mínimo. Segundo: coisas que não são importantes devem ficar fora do caminho crítico. O motor não pode parar porque o compressor do ar-condicionado quebrou. No navegador, isso é muito difícil de alcançar, mas ainda vale a tentativa

  • Pior ainda, o PostHog parece atualizar dinamicamente parte do próprio código em tempo de execução, em vez de empacotar tudo no build
    A documentação menciona uma opção avançada para incluir todas as dependências no build. Entendo por que isso existe, e posso estar entendendo errado, mas, do ponto de vista do usuário, eu esperaria que o lazy loading de código em execução fosse uma opção de otimização, não o padrão. Na minha visão, isso só deveria ser usado quando um bundle completo causasse uma latência de entrega séria

    • Essas lições claramente têm valor, mas depois aparece alguém do marketing exigindo que coloquem PostHog ou outro script de rastreamento no site, e não aceita um não como resposta
  • O bug parece ter estado dentro de um window.fetch monkey-patchado
    https://github.com/PostHog/posthog-js/blob/759829c67fcb8720f...
    A maior lição aqui é que, se você vai criar uma biblioteca popular e fazer monkey patch de função global, os testes precisam ser realmente muito bons
    “Vamos colocar a chamada do PostHog dentro de um try/catch, por via das dúvidas” e “Por causa do PostHog, literalmente não dá para enviar requisições POST com fetch()” são coisas completamente diferentes

    • Fui olhar por que isso não foi pego nos testes, e até uma chamada comum de fetch podia gerar erro. Além de faltar cobertura de teste para as várias formas de usar fetch, parece que mocking excessivo também contribuiu: https://github.com/PostHog/posthog-js/blob/main/src/_tests...
      As funções de fetch e XHR são totalmente mockadas para não fazer nada, então obviamente isso não detecta problemas que surgem na interação com código nativo subjacente ou com outras bibliotecas. O Cypress também está configurado, então não entendo por que tentariam mockar APIs do navegador
    • Obrigado por apontar isso. Não li o post com atenção, mas fiquei curioso sobre como uma biblioteca de monitoramento conseguiu derrubar a aplicação inteira
      Se estivesse integrada de forma razoável, eu imaginaria que, no pior caso, só o processamento dos eventos de monitoramento falharia
      O fato de o PostHog fazer patch de uma função global tão importante deveria ser uma funcionalidade muito bem documentada. Assim, quem usa a ferramenta saberia disso e poderia ao menos considerar essa hipótese ao depurar problemas aparentemente difíceis de explicar
    • Parece ter funcionado como definido. Não foi uma forma de hogar requisições POST?
    • Isso é comum nesse conjunto de ferramentas de analytics. Não sei como dá para realmente testar tudo quando se mexe numa API tão central assim
      Por exemplo, o Heap Analytics ainda hoje, neste mês, mexe em alguma coisa dentro do Hotwire de um jeito que quebra o Hotwire aleatoriamente e faz com que todos os cliques virem carregamentos completos de página. Na minha experiência, isso afeta entre 30% e 60% dos carregamentos. Dá para corrigir, mas eu precisei de mais de 50 horas de depuração até conseguir fazer o Heap carregar depois de todo o JavaScript do Hotwire
  • Como outros disseram, o bug que levou a esse estresse madrugada adentro foi uma mudança de uma linha na biblioteca PostHog[0]
    Eu vejo isso como mais um lembrete da importância de dar nomes exatos às variáveis
    O código res = await originalFetch(url, init) parece inofensivo o bastante. Mas, como a declaração TypeScript mostra, o parâmetro url não é necessariamente uma URL: url: URL | RequestInfo
    Quando não é uma URL, mas um objeto RequestInfo, aí surge o problema. Isso porque um objeto Request já foi criado antes no início da implementação da função e acabou sendo “consumido”, então não pode ser usado de novo aqui
    Se o parâmetro tivesse um nome mais preciso, como urlOrRequestInfo, teria sido bem mais difícil deixar passar esse problema nessa mudança
    Numa linha bem mais especulativa, como os tipos lineares vindos da lógica linear podem formalizar que um valor foi “consumido”, talvez um sistema de tipos apropriado pudesse evitar esse tipo de bug
    [0] https://github.com/PostHog/posthog-js/pull/1351/commits/2497...

    • O problema com sistemas de tipos lineares/afins é que a barreira de entrada é absurdamente alta
      Basta olhar para a semântica de ownership em linguagens como Rust. Não é algo impenetrável, e especialmente com experiência fica melhor, mas ainda assim pesa bastante, a ponto de ser uma das maiores reclamações de quem está aprendendo
  • Foi um texto estressante, mas também engraçado. Só que a parte de se culpar me pareceu familiar demais
    Eu mantenho um app iOS/macOS relativamente bem-sucedido e já empurrei um release que quebrou completamente mais de 350 mil instalações. Não foi 100% minha culpa, mas era o meu produto, então na prática não fez muita diferença
    O suor frio e a vergonha naquela hora foram intensos de verdade. E, por ser App Store, a correção ainda precisava passar por revisão, o que aumentou ainda mais o tempo. Felizmente, 30 minutos depois do envio o app já entrou em revisão e foi aprovado poucos minutos depois

    • Meu primeiro emprego como desenvolvedor foi me deixar, não por eu ser brilhante, mas por incompetência, “ter permissão” para estragar as coisas dos clientes quando eu era jovem
      Conforme avancei na carreira e fui para liderança, percebi que isso foi uma experiência extremamente valiosa. Talvez eu tenha me estressado na época, mas agora essa memória já está tão distante que mal consigo alcançá-la. Hoje definitivamente não me estressa
      Isso pode ser controverso, mas às vezes eu deliberadamente permito que alguém do time em início de carreira quebre a produção. Digo, quando consigo ver isso de antemão e tenho confiança de que vamos nos recuperar rápido
      É senso comum dizer que é importante dar espaço para falhar, mas muitos líderes traçam a linha quando a falha afeta clientes reais. Se você não está construindo algo crítico como software para pousar aviões — o que é uma situação muito comum e afortunada — então o time precisa passar pela experiência de um incidente em produção, mesmo que isso custe a alguém em Spokane, no estado de Washington, alguns minutos sem conseguir usar o produto
  • Obrigado por escrever isso. Gosto especialmente de ler como as pessoas atravessam esse tipo de desafio sob pressão, geralmente virando a noite
    Não só pela análise técnica pós-incidente, mas porque isso ficou melhor por também trazer a perspectiva humana que normalmente é apagada dessas histórias. Esse tipo de narrativa técnica é o tipo de coisa que só desenvolvedores independentes/solo ou fundadores conseguem compartilhar com liberdade

  • Só pela forma como rastreou o problema já dá para ver que ele era, antes de tudo, um programador. Foi para o próprio código e foi para os logs. Ambos são caminhos racionais e ambos podem apontar a causa, mas ele deixou passar a pista mais importante que tinha: “funcionava em localhost”
    SRE, DevOps, engenheiro de plataforma, ou qualquer que seja o título do dia, eu teria focado nas diferenças entre o sistema que funcionava e o sistema que não funcionava. Teria adicionado e removido uma diferença de cada vez, ou removido e depois recolocado, até algo voltar a funcionar
    Eu vejo duas coisas. 1) Existe um ambiente que funciona. 2) O ambiente que falha também funcionava antes e começou a falhar depois
    Isso não quer dizer que meu método seja superior. Só quero mostrar a diferença na forma de encarar o problema. Os dois vão afunilando a busca a partir do que conhecem. Eu conheço sistemas, você conhece código

  • Há muito tempo, quando eu trabalhava como técnico em eletrônica, havia uma pilha de placas de processador do Perkin Elmer 7/32 retiradas de serviço. Eram placas com defeito, de várias revisões, e para cada placa só havia o esquema elétrico de uma das revisões
    Eu achei que não tinha salvação, mas um técnico mais velho e mais sábio me ensinou um método
    Coloque a placa boa em um extensor e rode em loop o programa de diagnóstico que falha. Observe e anote, com um osciloscópio, todos os pinos do conector. Troque pela placa defeituosa e repita
    Que sinal está diferente? Siga esse sinal para trás. Se o esquema não bater, desenhe um esquema que reflita a fiação real usando um voltímetro e observação visual
    Ele chamava isso de “placa boa - placa ruim”, e realmente funcionava. Não vou dizer que era eficiente em termos de custo, mas consertamos todas as placas e minha habilidade de resolver problemas em circuitos eletrônicos digitais aumentou muito
    Era um tipo de trabalho de “bombeiro”. Você ficava esperando o sistema quebrar, então não importava se 2 técnicos passassem 1 semana em uma única placa de circuito

  • “Vamos voltar para a versão de um mês atrás. Não funciona. E a de três meses? Também não. Continua falhando. E a de um ano? Nada feito.”
    Ele só reverteu o próprio código e continuou usando a atualização do PostHog que quebrou no mesmo dia? A lição que eu tiro disso é que você precisa conseguir reverter tudo, inclusive as dependências

  • É um bom texto porque faz a gente lembrar das pessoas por trás do serviço e também mostra bem o processo de depuração
    Na prática, pressão não faz ninguém depurar um problema mais rápido. Normalmente, ela atrapalha o raciocínio. É preciso ignorar o resultado o máximo possível e manter a calma
    A maioria de nós já passou por algo parecido, em maior ou menor grau. Claro que o estresse de tocar a própria empresa deve ser especialmente pesado