1 puntos por GN⁺ 2024-08-21 | 1 comentarios | Compartir por WhatsApp
  • En jumpcomedy.com, alrededor de las 10 p. m., todas las llamadas HTTP POST de RTK Query empezaron a fallar y las funciones del sitio se rompieron; como en local todo funcionaba bien, fue difícil rastrear la causa.
  • Mientras se acumulaban las quejas de clientes, el operador tuvo que enfrentar la caída solo, sin soporte de producción, SRE, ingenieros senior ni managers.
  • El TypeError relacionado con fetch en el navegador no dio una pista directa, ya que GET y DELETE funcionaban con normalidad.
  • Aunque se revisaron Sentry, la base de datos de producción, Cloudflare, una actualización de Chrome y hasta un rollback a versiones anteriores, nada cambió; al agregar la api_key de PostHog que estaba vacía en local, el problema se reprodujo.
  • Después de quitar PostHog, la funcionalidad volvió a la normalidad, y más tarde se confirmó en issues de GitHub de PostHog y Redux Toolkit que se trataba de una afectación causada por una herramienta externa.

La presión que provocó la caída

  • En jumpcomedy.com, desde alrededor de las 10 p. m., todas las llamadas HTTP POST basadas en RTK Query empezaron a fallar y las funciones principales dejaron de funcionar correctamente.
  • Había cambios desplegados recientemente, pero no parecían explicar el problema, y como en el entorno local no se podía reproducir, el rastreo se volvió aún más difícil.
  • Se pidió ayuda en los Discord de NextJS y Vercel, pero no hubo respuesta, y tampoco existía un equipo de soporte de producción al que escalar la atención del incidente.
  • Los correos de clientes seguían acumulándose.
    • Consultas sobre que no podían cambiar el precio de un evento.
    • Consultas sobre que no podían eliminar un código promocional.
  • Como pequeños negocios dependían del servicio, el operador sintió vergüenza, tristeza, impotencia y síndrome del impostor.

Proceso de depuración y confirmación de la causa

  • El error del navegador era un TypeError que indicaba que fetch se estaba ejecutando con un request object ya usado, pero no apuntaba a la causa real.
  • Se agregaron varios console.log() y breakpoints para revisar headers, la longitud del token de API y el orden de las llamadas, pero no llevaron a una causa clara.
  • Se sospechó de una posible actualización de Chrome, pero como también se reprodujo en Firefox y Edge, no era un problema exclusivo del navegador.
  • El fallo continuó incluso al volver a versiones anteriores.
    • También falló una versión de hace un mes.
    • También falló una versión de hace tres meses.
    • También falló una versión de hace un año.
  • Para reducir las diferencias entre local y producción, se revisaron varios candidatos.
    • Quitar Sentry en producción: sin cambios.
    • Conectar local a la base de datos de producción: sin cambios.
    • Desactivar Cloudflare: sin cambios.
  • En local, para ahorrar costos, la api_key de PostHog se dejaba vacía, y al agregarla se reprodujo el mismo problema.
  • Al quitar PostHog en el siguiente commit, toda la funcionalidad volvió a operar con normalidad.
  • Más tarde, el mismo problema también se confirmó en GitHub issues.

1 comentarios

 
GN⁺ 2024-08-21
Opiniones de Hacker News
  • Después de trabajar durante un año como SRE en una gran empresa global, pude salir del modo de “pánico” del que habla el artículo.
    Desde el punto de vista del negocio, todos los problemas parecen eventos de fin del mundo, y es fácil entrar en pánico en esa situación, pero en realidad rara vez son tan malos y, aunque lo sean, por lo general se sobrevive sin mayores daños.
    En estas situaciones, la clave es detenerse 5 a 10 minutos antes de meter mano para arreglarlo de inmediato e intentar dibujar la situación con la mayor claridad posible. El miedo interfiere con el juicio racional, y si empiezas a apretar botones al azar en estado de pánico, puedes complicar más el problema. Mi truco es echarme agua muy fría en la cara y las manos para cortar el circuito del miedo.
    Después de pasar por esto algunas veces, te das cuenta de que no es tan grave como parece y ganas la confianza de haber manejado situaciones malas antes, así que sabes que puedes con ello incluso si no hay nadie a quien pedir ayuda.

    • Si algo se “rompe”, la empresa puede armar un escándalo, pero hay que recordar que no se alarma en absoluto por problemas que quizá sean más importantes.
      Cosas como software comprado que no sirve para nada por mala asignación de personal o fallas de configuración, empleados que pierden miles de horas al año por malas experiencias de usuario y requisitos sin sentido, capacidades que no hacen nada y quedan abandonadas, reuniones inútiles que desperdician tiempo todos los días, funciones que existen solo para cumplir requisitos de auditoría, o ejecutivos que siguen despilfarrando dinero de la empresa.
      El downtime no parece peor que estos problemas, pero atrae mucha más atención y pánico. Se siente como el contraste entre el terrorismo y las enfermedades cardíacas. A la empresa no le importa tu sueño ni tu salud mental, y te va a presionar tanto como pueda. No digo que sea maliciosa, pero en este punto se comporta como un abusador: cuanto más cedes, más te presiona.
    • Los peores errores que he visto durante incidentes reales casi siempre vinieron de una sobrerreacción.
      Uno de mis lemas al programar es “nada de magia negra”. Si no entiendes por qué funciona, no está terminado.
      Veo la respuesta a incidentes de la misma manera. Si no puedes explicar de forma coherente por qué la propuesta de alguien tendría un efecto, creo que no deberías ejecutarla. Quizá algún día llegue el momento en que simplemente haya que apretar el gatillo, pero al mirar atrás, creo que al final nunca se dio un caso así.
      Fue bastante impactante ver a altos ejecutivos, que normalmente eran muy serenos, empezar a lanzar candidatos de corrección al azar durante un incidente.
    • En cambio, las personas en jumpcomedy.com que tenían que cambiar el precio de un evento a las 2 de la mañana, más o menos dos zonas horarias, se habrán sentido muy decepcionadas. Algunas de ellas quizá incluso murieron.
      Imagina cuánto mayor habría sido el daño si alguien hubiera detenido a este desarrollador independiente diciéndole que “no intentara poner de moda fetch”.
    • Uno de los VP más geniales que conozco decía a menudo: “lento es suave, y suave es rápido”.
      Es cierto que el miedo interfiere con el juicio racional, y a eso quisiera agregar que el miedo es extremadamente contagioso. Cuando quienes ejecutan el trabajo ven que líderes, gerentes o colegas entran en pánico, con frecuencia también entran en pánico. Por suerte, mi VP siempre se mantuvo tranquilo y priorizó la claridad por encima de la acción.
    • Al final, no eres tú quien asume ese riesgo. No es tu empresa, y la empresa puede dejarte fuera en cualquier momento, y de hecho lo hará. Claro, salvo que sea tu empresa.
  • No estoy seguro de que esto sea una crisis mental, y podría dar una impresión equivocada a personas que sí sufren crisis reales por estrés relacionado con la tecnología.
    En mi caso me ocurrió una sola vez, y fue un ataque de ansiedad. Tuve mucha suerte de que mi esposa estuviera a mi lado explicándome la situación y ayudándome a entender lo que estaba viviendo. Ella lo había pasado varias veces, pero para mí fue la primera y, por suerte, la última.
    Estas cosas le pueden pasar a cualquiera, y no hay nada malo en ello. Es muy importante interiorizar que no significa que tengas un defecto ni que seas débil.
    En mi caso, lo que finalmente logró detenerlo fue Xanax, y como pude dormir, creo que vale la pena tenerlo a mano.
    Lo que quiero decir es que hay una diferencia entre pensamientos intrusivos y un estado realmente incontrolable e incapacitante, como un ataque de ansiedad o un ataque de pánico. Si eso ocurre, no podrás trabajar, y está bien.

    • https://www.webmd.com/mental-health/signs-nervous-breakdown
      No todas las crisis vienen en forma de ataques de pánico o ansiedad. Pueden manifestarse así, pero no es la única manera. El estrés se presenta de formas muy distintas según la persona, e incluso según el factor estresante.
      Como no podemos saber qué ocurrió realmente dentro de la cabeza de esa persona, “diagnosticar” desde afuera es casi imposible. Aunque no haya sido un ataque de pánico completo, sí suena como si hubiera quedado funcionalmente paralizada durante horas.
    • Hay que tener cuidado con decir que “conviene tener Xanax cerca”.
      Si lo buscas en línea, parece que Xanax puede ser adictivo.
      https://www.drugs.com/xanax.html
      No parece ser algo que se deba tomar a la ligera.
    • No debería tener que tomar medicamentos para soportar funciones que se lanzan siempre a medias, cambios metidos sin pensarlo y las consiguientes alertas de PagerDuty a las 3 de la mañana.
      Es muy probable que veamos morir por enfermedades relacionadas con el estrés a un gran grupo que entró a la industria tecnológica a mediados de los 2000.
    • Una de las cosas que no esperaba al trabajar en una empresa de tecnología enterprise fue cuántos colegas toman Xanax con regularidad.
      Como alguien que ha tenido mucha ansiedad toda la vida, la idea de que una pastilla adictiva pueda quitarla toda me da miedo. Siento que terminaría aferrado a ella para siempre.
    • La verdad, me decepcionó que fuera una historia común de depuración de dependencias. Algunas veces en el pasado sentí que estuve al borde de una crisis, así que esperaba un texto con el que pudiera sentirme más identificado.
  • El estrés de esta persona surgió por una sola línea de código de PostHog. El commit revertido es este: https://github.com/PostHog/posthog-js/pull/1371/commits/7598...
    Veo dos lecciones aquí. Primero, si lo despliegas, lo posees. Así que cuanto menos despliegues, mejor, y debes minimizar las dependencias. Segundo, hay que mantener lo que no es importante fuera del camino crítico. El motor no debería detenerse porque se descompuso el compresor del aire acondicionado. En el navegador es muy difícil lograrlo, pero vale la pena intentarlo.

  • Peor aún, PostHog parece actualizar dinámicamente parte de su propio código en tiempo de ejecución, y no incluirlo en el bundle durante el build.
    La documentación tiene una opción avanzada para incluir todas las dependencias en el build. Entiendo por qué lo hacen, y puede que yo haya entendido mal, pero como usuario esperaría que la carga diferida de código ejecutable no fuera el valor predeterminado, sino una opción de optimización. Creo que debería usarse solo cuando un bundle completo genera demoras de entrega graves.

    • Estas lecciones claramente son valiosas, pero después viene alguien de marketing y exige poner PostHog u otro script de rastreo en el sitio, y no acepta un no como respuesta.
  • Parece que el bug estaba dentro del window.fetch monkey-patcheado.
    https://github.com/PostHog/posthog-js/blob/759829c67fcb8720f...
    La mayor lección aquí es que, si vas a crear una biblioteca popular y haces monkey-patching de funciones globales, tus pruebas tienen que ser realmente buenas.
    “Pongamos las llamadas a PostHog en un try/catch por si acaso” y “por culpa de PostHog literalmente no puedes enviar requests POST con fetch()” son cosas completamente distintas.

    • Miré un poco para ver por qué esto no se detectó en las pruebas, y al parecer incluso una llamada fetch común podía fallar. Además de la falta de cobertura para las distintas formas en que se puede usar fetch, parece que el exceso de mocks también contribuyó: https://github.com/PostHog/posthog-js/blob/main/src/_tests...
      Como las funciones fetch y XHR completas estaban mockeadas para que no hicieran nada, obviamente no iban a detectar problemas que surgen al interactuar con APIs nativas subyacentes u otras bibliotecas. También tienen Cypress configurado, así que no sé por qué querrían mockear APIs del navegador.
    • Gracias por señalar esto. No leí el artículo en detalle, pero me preguntaba cómo una biblioteca de monitoreo podía tirar abajo toda la aplicación.
      Si estuviera integrada de forma razonable, pensaba que, en el peor de los casos, fallaría el procesamiento de eventos de monitoreo.
      Que PostHog parchee una función global tan importante debería estar bien documentado como funcionalidad. Así quienes lo usan lo sabrían y podrían tenerlo razonablemente en mente al depurar problemas que, en apariencia, son difíciles de explicar.
    • Parece que funcionó según lo definido. ¿No habrá hogueado las requests POST?
    • Es algo común en este tipo de suites de analítica. No sé cómo se puede probar todo de verdad cuando tocas una API tan central.
      Por ejemplo, Heap Analytics todavía, a este mes, toca algo dentro de Hotwire y rompe Hotwire por completo de forma aleatoria, haciendo que todos los clics se conviertan en cargas de página completas. En mi experiencia, afecta entre el 30% y el 60% de las cargas de página. Se puede arreglar, pero tuve que dedicar más de 50 horas a depurar hasta lograr que Heap cargara después de todo el JavaScript de Hotwire.
  • Como dijeron otras personas, el bug que llevó a este estrés de madrugada fue un cambio de una línea en la biblioteca PostHog[0].
    Yo lo veo como un recordatorio de la importancia de ponerles nombres precisos a las variables.
    El código res = await originalFetch(url, init) parece bastante inofensivo. Pero, como muestran las declaraciones de TypeScript, el parámetro url no necesariamente es una URL: url: URL | RequestInfo.
    El problema aparece cuando no es una URL sino un objeto RequestInfo. Porque al principio de la implementación de la función ya se creó un objeto Request y este ya fue “consumido”, por lo que no se puede volver a usar aquí.
    Si el parámetro hubiera tenido un nombre más preciso, como urlOrRequestInfo, habría sido más difícil pasar por alto el problema en este cambio.
    Como idea mucho más especulativa, dado que los tipos lineales provenientes de la lógica lineal pueden formalizar que un valor fue “consumido”, tal vez un sistema de tipos adecuado también podría evitar este tipo de bugs.
    [0] https://github.com/PostHog/posthog-js/pull/1351/commits/2497...

    • El problema de los sistemas de tipos lineales/afines es que la barrera de entrada es enorme.
      Basta ver la semántica de ownership de lenguajes como Rust. No es impenetrable, y mejora especialmente con la experiencia, pero es una carga importante, al punto de ser una de las cosas de las que más se quejan quienes están aprendiendo.
  • Fue un texto estresante pero también gracioso. Dicho eso, la parte de culparse a uno mismo me resultó demasiado familiar.
    Mantengo una app de iOS/macOS bastante exitosa, y una vez saqué un release que rompió por completo más de 350 mil instalaciones. No fue totalmente mi culpa, pero como era mi producto, no había mucha diferencia.
    El sudor frío y la vergüenza de ese momento fueron tremendos. Además, al ser la App Store, el fix también tenía que pasar por revisión, lo que alargaba los tiempos. Por suerte entró en revisión 30 minutos después de enviarlo y fue aprobado en unos minutos.

    • Mi primer trabajo como desarrollador me “permitió” romper cosas de clientes a temprana edad no porque yo fuera inteligente, sino porque era incompetente.
      A medida que avancé en mi carrera y pasé a liderazgo, me di cuenta de que esa fue una experiencia muy valiosa. Quizás antes me estresaba, pero ahora ese recuerdo está tan lejos que ni me alcanza. Hoy definitivamente no me estresa.
      Puede ser polémico, pero a veces permito que alguien del equipo en una etapa temprana de su carrera rompa el entorno de producción. Cuando lo veo venir y estoy seguro de que podemos recuperarnos rápido.
      Es de sentido común decir que es importante dar espacio para fallar, pero muchos líderes trazan la línea cuando el fallo afecta a clientes reales. Si estás en la situación muy común y afortunada de no estar construyendo algo crítico, como software que aterriza aviones, entonces aunque alguien en Spokane, Washington, tenga que pagar el precio de no poder usar el producto durante unos minutos, hay que dejar que el equipo experimente un incidente de producción.
  • Gracias por escribir algo así. Me gusta leer cómo la gente supera este tipo de desafíos, especialmente bajo presión y, por lo general, durante toda la noche.
    Me pareció aún mejor porque no solo incluye un análisis técnico post mortem, sino también la perspectiva humana que normalmente se borra de estas historias. Este tipo de relato técnico es algo que probablemente solo desarrolladores independientes, equipos pequeños o fundadores pueden compartir con libertad.

  • Solo por la forma en que rastreó el problema, se nota primero que es programador. Fue a su propio código y fue a los logs. Ambas cosas son razonables y ambas podían ser la causa, pero pasó por alto la pista más importante que tenía: “funcionaba en localhost”
    Como SRE, DevOps, ingeniero de plataforma, o el cargo que toque ese día, yo me habría concentrado en la diferencia entre el sistema que funciona y el que no funciona. Habría agregado y quitado diferencias una por una, o las habría quitado y vuelto a agregar, hasta que algo funcionara
    Lo que veo son dos cosas: 1) hay un entorno que funciona. 2) el entorno que falla originalmente funcionaba y luego empezó a fallar
    No significa que mi método sea superior. Solo quiero mostrar la diferencia en la forma de ver el problema. Ambos vamos acotando alrededor de lo que conocemos. Yo conozco los sistemas, tú conoces el código

    • Hace mucho, cuando trabajaba como técnico electrónico, había una pila de placas de procesador Perkin Elmer 7/32 retiradas de servicio. Eran placas defectuosas, con varias revisiones, y para cada placa solo había un diagrama de circuito de una revisión
      Yo lo veía sin esperanza, pero un técnico mayor y más sabio me enseñó un método
      Poníamos una placa buena en un extensor y corríamos en bucle el programa de diagnóstico que fallaba. Con un osciloscopio revisábamos y registrábamos todos los pines del conector. Cambiábamos a una placa mala y repetíamos
      ¿Qué señal era diferente? Seguíamos esa señal hacia atrás. Si el diagrama no coincidía, con un voltímetro y a simple vista dibujábamos un diagrama que reflejara el cableado real
      Él llamaba a esto “tarjeta buena - tarjeta mala”, y de verdad funcionaba. No diría que fuera rentable, pero arreglamos todas las placas y mi capacidad para resolver problemas de circuitos electrónicos digitales mejoró muchísimo
      Era una especie de trabajo de “bombero”. Como consistía en esperar a que un sistema se rompiera, no importaba que 2 técnicos dedicaran una semana a una sola placa de circuito
  • “Volvamos a la versión de hace un mes. No funciona. ¿La de hace tres meses? No funciona. Sigue fallando. ¿La de hace un año? Para nada.”
    ¿Solo estaba revirtiendo su propio código y seguía usando la actualización de PostHog que se había roto el mismo día? Mi lección es que hay que poder revertir todo, incluidas las dependencias

  • Es un buen texto que vuelve a recordarnos a las personas detrás del servicio, y también muestra bien el proceso de depuración
    Siendo realistas, la presión no ayuda a depurar un problema más rápido. Por lo general entorpece el pensamiento. Hay que ignorar el resultado todo lo posible y mantenerse tan tranquilo como se pueda
    La mayoría de nosotros, en mayor o menor medida, probablemente hemos pasado por situaciones parecidas. Claro que el estrés de operar tu propia empresa debe ser especialmente grande