PRODPlataforma BaaS soberana europeaAbrir panel →

Rendimiento · 10 lectura mínima

¿Por qué ningún recolector de basura cambia la latencia p99?

Affane Daylami · Fondateur · 2 de julio de 2026

volver al blog

p99 mide la solicitud más lenta entre cien: la que incumple su SLA mientras el promedio sigue siendo perfecto. En un backend con mucho tráfico, este puñado de solicitudes lentas a menudo tiene una única causa: el recolector de basura, que interrumpe todo el programa para liberar memoria. Rust no tiene recolector de basura. Aquí está el mecanismo, con fuentes de terceros fechadas en lugar de una cifra de marketing.

Este texto en inglés se generó automáticamente a partir del original en francés y aún no ha sido revisado.
Esta página fue traducida automáticamente. La versión en inglés es autorizada.

Este artículo se basa únicamente en fuentes fechadas de terceros, nunca en un cifrado Aurabase inventado. No se incluye latencia p99 específica de nuestro backend. Escribimos el núcleo de nuestra aplicación en Rust, sin recolector de basura: un hecho que se puede verificar directamente en el código, el espacio de trabajo Cargo y los servicios axum. Sin embargo, todavía no hemos publicado una metodología de referencia p99 reproducible para demostrar esto en cifras. Este texto explica un mecanismo, no un resultado medido.

Lo esencial

  • p99 mide la consulta más lenta entre cien: el lugar donde más duele una pausa del recolector de basura (GC), no en promedio (Dean & Barroso, “The Tail at Scale”, Google, 2013).
  • Un GC interrumpe todo el programa (“detener el mundo”) para liberar memoria no utilizada. Rust no tiene GC: la memoria se libera en el momento preciso en que un valor sale del alcance, verificado por el compilador.
  • Discord documentó un servicio de caché en 2020 en el que Go activaba un ciclo de recolección de basura al menos cada dos minutos, y cada ciclo provocaba un aumento en la latencia (Blog de ingeniería de Discord).
  • Reducir una pausa de GC requiere años de ingeniería, incluso en Google: el recolector de GB pasó de 300-400 ms a 500 µs entre 2015 y 2018, sin llegar nunca a cero (go.dev).
  • El núcleo backend de Aurabase está escrito en Rust, sin recolector de basura, verificado en el código. Hasta la fecha no se han publicado cifras de latencia de Aurabase p99: esto sigue siendo un mecanismo, no una medición.
#
Latencia de cola

¿Por qué p99 no es promedio?

Un promedio esconde lo esencial. Si 99 solicitudes de 100 responden en 5 ms y solo una tarda 500 ms, el promedio sigue siendo bajo. Pero uno de cada cien usuarios experimenta una espera cien veces más larga. El p99 mide exactamente esta solicitud: la centésima más lenta, la que viola su SLA mientras su panel de latencia promedio permanece verde.

En Google, Jeffrey Dean y Luiz André Barroso formalizaron este problema en “The Tail at Scale” (Comunicaciones de la ACM, vol. 56, 2013). Su observación, citada a menudo desde entonces: “Los episodios temporales de alta latencia que no son importantes en sistemas de tamaño moderado pueden llegar a dominar el rendimiento general del servicio a gran escala”. En resumen: episodios ocasionales de latencia, insignificantes a pequeña escala, acaban dominando el rendimiento percibido de un sistema distribuido.

Un backend que procesa miles de solicitudes por segundo seguramente enviará, en un momento u otro, una solicitud que cae durante una pausa de GC. A gran escala, este no es un caso infrecuente. Ésta es una certeza estadística.

#
Mecánica de GC

Qué hace un recolector de basura y por qué pausa todo

Un recolector de basura (GC) rastrea continuamente los objetos vivos de un programa (aquellos a los que todavía se hace referencia en algún lugar) y libera la memoria de los objetos que se han vuelto inaccesibles. Este seguimiento se llama tracing: el GC recorre el gráfico de referencia, marca lo que todavía se utiliza y luego barre el resto.

El problema: recorrer este gráfico mientras el programa continúa creando nuevas referencias produce resultados inconsistentes. La respuesta histórica, que muchos GC modernos todavía utilizan como último recurso, es stop-the-world: todo el programa se detiene mientras marca y escanea. Cuanto mayor es el montón, más larga tiende a ser la pausa: su duración depende del tamaño de los datos en vivo, no de la carga de trabajo actual.

La mayoría de los GC modernos utilizan una estrategia generacional: suponen que la mayoría de los objetos mueren jóvenes. Por lo tanto, las asignaciones recientes se escanean con frecuencia, pero rápidamente, en un área de memoria pequeña. Los objetos que sobreviven a varios ciclos migran a un área más grande y se escanean con poca frecuencia, pero cuando es necesario limpiar esa área, la pausa asociada crece con su tamaño. Es esta ruptura “importante”, no las pequeñas rupturas “menores”, la que domina el p99 de un servicio de alto tráfico y alta asignación.

Información

Los GC generacionales y concurrentes modernos reducen la frecuencia y duración de estas pausas al trabajar en paralelo con el programa. Pero casi todos mantienen un mecanismo alternativo para detener el mundo en casos límite, y reducirlo requiere años de ingeniería. La sección 04 ofrece un ejemplo cuantificado y con fuentes.

#
caso real

Discord, 2020: una interrupción de GC se convierte en un incidente de producción

En febrero de 2020, el ingeniero Jesse Howarth publicó una publicación que se ha convertido en una referencia en la industria: “Por qué Discord está cambiando de Go a Rust” (Blog de ingeniería de Discord). El servicio correspondiente, Read States, gestiona el estado de lectura de mensajes de millones de usuarios (decenas de millones de entradas por caché) con cientos de miles de actualizaciones por segundo.

El diagnóstico es directo, citado tal como está en el artículo: “Go forzará una recolección de basura cada 2 minutos como mínimo”. En otras palabras, Go activa un ciclo de recolección de basura al menos cada dos minutos en este servicio, y cada ciclo produce un aumento en la latencia visible en los gráficos del equipo.

El equipo primero redujo el tamaño del caché para suavizar los picos. El compromiso siguió siendo desfavorable: menos pausas de GC, pero más solicitudes de pérdida de caché que caen en la base de datos; por lo tanto, un p99 general degradado en otros lugares. La solución básica fue la reescritura del servicio en Rust, sin un recolector de basura que monitorear.

La publicación desató un animado debate técnico: más de 1580 puntos y 642 comentarios en Hacker News el mismo día de su publicación (4 de febrero de 2020), una señal de que el problema va mucho más allá del caso Discord.

#
Ir a la historia

Tres años de ingeniería en Google para bajar una pausa de 400 ms a 500 µs

El recolector de basura Go ilustra la magnitud del esfuerzo necesario para controlar una pausa de GC, incluso con los recursos de un equipo dedicado de Google. Rick Hudson, líder técnico de Go GC, documentó esta historia en dos publicaciones oficiales del blog de Go.

Antes de agosto de 2015300-400 msColeccionista histórico de Go, antes del rediseño
Agosto 2015 · Ir 1.530-40 msPrimer recopilador competidor, objetivo < 10 ms establecido
2016 · Ir 1.6< 10 ms (SLO mantenido)Objetivo inicial alcanzado en producción.
Marzo 2017 · Ir 1.8bajo el milisegundoEliminando el escaneo de pila de detener el mundo
Agosto 2017 · Ir 1.9100-200 µs (marca)Nuevo punto de referencia informal mencionado por el equipo
2018 · SLO anunciado500 µs por cicloObjetivo de servicio formalizado por Rick Hudson

Fuente: “Getting to Go: The Journey of Go’s Garbage Collector”, go.dev, 12 de julio de 2018; y “Go GC: Priorizar la baja latencia y la simplicidad”, go.dev, 31 de agosto de 2015.

Tres años de trabajo dedicado han reducido las típicas vacaciones en un factor de mil. Pero la pausa nunca desapareció: es un objetivo de servicio (SLO), no una garantía absoluta de cero. Un trazado de GC debe, por construcción, atravesar un gráfico de objetos vivos de vez en cuando. La única variable ajustable es la frecuencia y duración de este viaje, no su existencia.

Esta elección de prioridad no es neutral. Go se dirige principalmente a servicios de red y backends web, donde una pausa de varios cientos de milisegundos interrumpe directamente la experiencia del usuario; de ahí el enorme esfuerzo invertido en la latencia en lugar del rendimiento bruto de GC. Otros tiempos de ejecución administrados heredaron diferentes compensaciones, determinadas por sus casos de uso históricos, antes de recuperar terreno con sus propios recopiladores de pausa baja. El punto común sigue siendo el mismo: todos parten del rastreo de GC, por lo tanto, de un mecanismo de pausa que debe minimizarse y que nunca debe eliminarse mediante la construcción.

#
Mecánica del óxido

Por qué Rust no tiene este problema de construcción

Rust no reduce las pausas de GC: elimina el mecanismo que las provoca. El compilador rastrea, durante la compilación, quién es el propietario de cada valor de memoria: esto esownership. Cuando el propietario de un valor sale del alcance, Rust inserta automáticamente la llamada que libera esa memoria, en el mismo lugar del código binario. Este mecanismo se llama RAII (La adquisición de recursos es inicialización): la liberación es determinista, no está programada por un recolector de basura que se ejecuta en segundo plano.

Alexandru Nedelcu, autor de un blog técnico reconocido en el ecosistema Scala/Rust, resume la compensación en un artículo reciente: “La compensación que hace Rust es la facilidad de uso, en lugar de un rendimiento con latencia predecible y seguridad” (alexn.org, 21 de julio de 2026). Rust cambia parte de la simplicidad de la escritura por una latencia predecible.

El mismo artículo resume por qué los GC modernos no siempre son suficientes: "Los GC modernos intentan hacer su trabajo de forma incremental y simultánea, sin afectar al programa. Pero su capacidad es limitada, recurriendo a un ciclo de GC que detiene el mundo y que congela todo el programa, afectando así a la latencia".

Aquí está el mecanismo en unas diez líneas: un ejemplo genérico, no un extracto del código de Aurabase:

example.rsrust
struct Connection {
    id: u32,
}

impl Drop for Connection {
    fn drop(&mut self) {
        println!("connexion {} fermée", self.id);
    }
}

fn handle_request() {
    let conn = Connection { id: 42 };
    // ...procesa la solicitud...
} // conn queda fuera de alcance aquí: se ejecuta drop()
   // en el momento preciso, verificado por el compilador:
   // no a través de un ciclo de recolección de basura.

Matiz importante: no todo es gratis. Los tipos de recuento de referencias (Rc, Arc) añaden un pequeño coste a cada clonación y versión. Este costo sigue siendo local y determinista. Nunca hay una pausa que congele todo el programa mientras pasa por un montón de memoria.

Aclaración útil para un backend asíncrono: el tiempo de ejecución asíncrono de Rust (tokio, utilizado por todos los servicios de Aurabase) no tiene nada que ver con un recolector de basura. Programa tareas cooperativas en un grupo de subprocesos, pero nunca itera a través de un gráfico de objetos en vivo para liberar memoria. La confusión es común proveniente de ecosistemas donde el tiempo de ejecución asíncrono y el GC son administrados por la misma máquina virtual.

#
Implicación arquitectónica

Qué cambia esto para un backend de alto tráfico

En un backend que atiende miles de solicitudes simultáneas, la ausencia de GC elimina una variable de la ecuación p99. Ya no es necesario dimensionar un montón de memoria, ajustar las generaciones de un recopilador o monitorear un ciclo que puede caer en el peor momento. La latencia de una solicitud individual depende de su propio trabajo, no de algún evento global impredecible en otra parte del programa.

El núcleo backend de Aurabase aplica este principio: todos los servicios (aura-gateway, aura-auth, aura-db, aura-realtime, aura-storage, aura-functions, aura-ai...) están escritos en Rust, organizados en un único espacio de trabajo de carga . Esto se puede verificar directamente en el repositorio:

Cargo.tomltoml
[workspace]
resolver = "2"

members = [
    "services/aura-gateway",
    "services/aura-auth",
    "services/aura-db",
    # ...9 otros servicios + bibliotecas compartidas
]

[workspace.dependencies]
axum = { version = "0.8", features = ["ws", "multipart", "macros"] }

Extracto real de Cargo.toml, edición de espacio de trabajo 2021, solucionador v2: verificado en el repositorio de Aurabase.

Lo que este hecho no prueba, en esta etapa: una cifra de latencia p99 medida para Aurabase. Todavía no hemos publicado una metodología de referencia reproducible para nuestro propio backend; este es un trabajo en progreso, no un resultado disponible hoy. La ausencia de un recolector de basura es un mecanismo verificado en el código. Esto por sí solo no es evidencia de una latencia medida de p99. Tenga en cuenta esta distinción ante cualquier argumento de marketing sobre el tema, incluido el nuestro; consulte nuestra comparación técnica Aurabase vs Supabase para obtener detalles de la arquitectura.

Medir correctamente un p99 requiere su propia disciplina: condiciones de carga representativas, percentiles calculados sobre una ventana deslizante suficientemente amplia y un entorno de prueba cercano al de producción. Publicar una figura sin esta metodología es como publicar una figura de marketing. Esto es exactamente lo que nos negamos a hacer en este artículo.

#
Matiz

Lo que no soluciona la ausencia de GC

No-GC no es una varita mágica

Quitar el recolector de basura elimina solo una fuente de latencia de cola, no todas. Un backend de Rust aún puede mostrar un p99 degradado debido a una espera de red, un grupo de conexiones de Postgres saturado, un bloqueo de base de datos impugnado, una consulta SQL mal indexada o una llamada API lenta de terceros. El mecanismo descrito en este artículo elimina una causa estructural. No proporciona inmunidad frente a otros.

En Aurabase, por ejemplo, cada servicio se comunica con Postgres a través de un grupo de conexiones (sqlx) y con otros servicios a través de NATS JetStream. Un grupo de tamaño insuficiente, una suscripción NATS de consumo lento o una consulta SQL sin un índice adecuado producen cada uno su propio pico de latencia, independientemente de la ausencia de un recolector de basura.

La conclusión práctica: la ausencia de GC es una buena razón arquitectónica para elegir un backend Rust para un sistema compatible con p99. Esto no es, en sí mismo, una garantía de latencia, ni en Aurabase ni en ningún otro lugar. El método que importa sigue siendo el mismo: medir, publicar la metodología y luego corregir lo que revelan las mediciones. Si está migrando desde un backend con GC, nuestra guía de migración Supabase a Aurabase detalla qué está cambiando y qué permanece igual.

#
Preguntas frecuentes

Preguntas frecuentes: recolector de basura y latencia p99

¿La ausencia de un recolector de basura garantiza un p99 bajo?+
No. Elimina una fuente estructural de pausas impredecibles, pero otros factores (red, grupo de conexiones, bloqueos de bases de datos) también influyen en la latencia de cola. Consulte la sección anterior "Lo que ningún GC no soluciona".
¿Por qué Discord no ajustó su GC Go de manera diferente?+
El equipo intentó varios ajustes, incluida una reducción en el tamaño de la caché, documentada en su publicación de 2020. La compensación siguió siendo desfavorable: menos pausas de GC frente a más errores de caché. Reescribir en Rust eliminó el compromiso en lugar de moverlo.
¿Han resuelto el problema los GC modernos como Go?+
Lo redujeron enormemente (el GC de Go pasó de 300-400 ms a 500 microsegundos entre 2015 y 2018 (go.dev)), pero no lo eliminaron. Un rastreo de GC mantiene, por construcción, un mecanismo de pausa para casos extremos.
¿Aurabase ha publicado un punto de referencia de latencia p99?+
Todavía no. El hecho verificado en el código es la ausencia de recolector de basura (Rust, workspace Cargo, axum Services). La cifra de latencia p99 medida y su metodología reproducible aún no se han publicado; preferimos ninguna cifra a una cifra sin fuente.

¿LISTO PARA IMPLEMENTAR?

Tu backend en cinco minutos.

No se requiere tarjeta de crédito · 500 MB gratis · 50,000 MAU