2 puntos por GN⁺ 2025-07-07 | 1 comentarios | Compartir por WhatsApp
  • Jake Gold ejecutó un programa CGI de Go + SQLite en un AMD 3700X de 16 hilos para comprobar cuánto puede aguantar el CGI al estilo de los años 90 en hardware moderno
  • El resultado muestra que incluso con hardware común se pueden procesar más de 2400 solicitudes por segundo y más de 200 millones al día
  • CGI inicia, ejecuta y termina un proceso por cada solicitud, por lo que en el pasado su sobrecarga era alta; para reducirla surgieron enfoques como PHP y FastCGI
  • Simon Willison contó que en 2020, en datasette-ripgrep, procesó búsquedas invocando el CLI de ripgrep basado en Rust, lo que le hizo replantearse la vieja creencia de que había que evitar ejecutar procesos durante una solicitud web
  • Con lenguajes de arranque rápido como Go y Rust, y en entornos con múltiples CPU, el procesamiento estilo CGI puede ser una opción más realista que antes, aunque no es la recomendación general

Rendimiento de CGI visto de nuevo en hardware moderno

  • Serving 200 million requests per day with a cgi-bin es un artículo en el que Jake Gold prueba el rendimiento del CGI al estilo de los años 90 con un programa CGI de Go + SQLite
  • El entorno de prueba fue un sistema basado en AMD 3700X de 16 hilos
  • El resultado clave es que, incluso con CGI, se pueden procesar más de 2400 solicitudes por segundo y más de 200 millones de solicitudes al día en hardware común
  • CGI funciona iniciando un proceso separado para cada solicitud entrante, ejecutándolo y luego finalizándolo
  • La comunidad web de los primeros años creó PHP y FastCGI para evitar esa sobrecarga, manteniendo el código residente en memoria y reduciendo costos adicionales

Por qué CGI podría no ser tan malo como antes

  • Uno de los cuellos de botella históricos de CGI era que los scripts web se escribían en lenguajes como Perl, Python y Java, que no fueron diseñados con el objetivo de arrancar extremadamente rápido
  • Hoy, usando Go y Rust, el procesamiento de solicitudes al estilo CGI puede funcionar de manera mucho más eficaz
  • En 2020, Simon Willison creó datasette-ripgrep e invocó desde el shell la herramienta CLI ripgrep, escrita en Rust, para ejecutar búsquedas, con buenos resultados
  • Como los programas CGI se ejecutan como procesos separados, también pueden encajar bien en una arquitectura que aproveche varios CPU
    • Los servidores modernos pueden tener 384 hilos de CPU
    • Incluso una VM pequeña puede tener 16 CPU
    • El rendimiento de CPU y memoria también ha mejorado mucho frente al pasado
  • La forma de escribir aplicaciones web de 1998 también puede convertirse en un experimento interesante al combinarse con Go y Rust, aunque no es la opción predeterminada que la mayoría de los servicios debería seguir

1 comentarios

 
GN⁺ 2025-07-07
Opiniones de Hacker News
  • Incluso usando algo como Python, hoy en día CGI es bastante rápido
    Si iniciar un script CGI consume 400 ms de CPU y el servidor tiene 64 núcleos, puede atender 160 solicitudes por segundo, o 14 millones de hits al día por servidor
    Si un servicio web, excluyendo los recursos estáticos, sufre incluso con varios millones de solicitudes al día, el cuello de botella no es el tiempo de arranque del proceso CGI
    Antes habría dicho que era “una tecnología aburrida con soporte desde hace mucho en la biblioteca estándar de Python”, pero parece que los mantenedores restantes de Python consideran perjudiciales la estabilidad y la compatibilidad hacia atrás, y han ido eliminando de la biblioteca estándar módulos demasiado aburridos y estables
    El módulo cgi fue eliminado en Python 3.13
    Lo usé a diario durante la mayor parte de los últimos 25 años, así que todavía tengo la costumbre de usar Python para prototipar, pero ahora me da pena y estoy dudando entre JS y Lua

    • La justificación para eliminar cgi está en https://peps.python.org/pep-0594/#cgi
      Curiosamente, ahí enlazan a https://peps.python.org/pep-0206/, del 14 de julio de 2000, es decir, de hace 25 años, donde ya describían el paquete cgi como “mal diseñado y ahora casi imposible de arreglar”
      El paquete https://github.com/jackrosenthal/legacy-cgi parece ofrecer un reemplazo drop-in del módulo de la biblioteca estándar
    • Lo que los mantenedores de Python eliminan es un módulo llamado cgi, no el soporte para implementar scripts CGI en sí
      Ese soporte sigue estando en CGIHTTPRequestHandler del módulo http.server
      Lo que había en el módulo cgi eran solo algunas funciones para parsear datos de formularios HTML
    • Se entiende la molestia por que Python elimine CGI de la biblioteca estándar
      Pero no entiendo muy bien que, como siguiente alternativa, se considere JS, que no tiene biblioteca estándar como tal
      Lua tampoco tiene un módulo CGI en su biblioteca estándar
    • Antes, la web de alto rendimiento era una especie de arte técnico
      Ahora se parece más a encontrar las partes absurdamente derrochadoras que se metieron para lanzar rápido y hacer que se detengan
      Si se intenta bien, una app casi podría no añadir latencia más allá del acceso al almacenamiento
    • En casos así prefiero más PHP o JS, que traen JIT por defecto
      Aprendí Python desde la versión 1.6, pero lo usé sobre todo para scripting de sistema operativo
      Entre 1999 y 2003 usé Tcl en módulos de Apache e IIS, y aprendí demasiadas lecciones por las malas mientras reescribía módulos una y otra vez en C
  • Hace poco probé en un mini servidor de 350 dólares un binario en Golang, RabbitMQ, Redis y MySQL, todo en la misma máquina, y sostuvo 5,000 solicitudes por segundo
    Extrapolado a 24 horas, son 400 millones de solicitudes al día
    Sorprende lo buenas que son las herramientas gratuitas de hoy, y también sorprende que aun así paguemos tanto dinero a los proveedores cloud
    No es una comparación exactamente igual, pero fue muy bueno desarrollar y tunear todo en una máquina del sótano

    • He visto demasiados casos donde despliegan un monstruo de microservicios en Kubernetes que hace 10 veces más lento el desarrollo, sin saber que un servidor puede manejar muchísimo más que 1 solicitud por segundo
      Es raro seguir aceptando ese overhead sin más razón que “porque Google lo hace así”
      Definitivamente tengo que escribir sobre la arquitectura de monolito modular que nos está funcionando bien
    • No necesariamente hay que pagarle a un proveedor cloud
      Puedes alquilar un servidor dedicado con una empresa de hosting y usarlo a tu gusto. Eso sí, quedas bastante atado a límites de ancho de banda o transferencia
      La nube se usa porque hay muchos intereses en juego. Por ejemplo, los VC y los inversionistas también tienen participación en empresas cloud, y temen que la inversión fracase por un pico de tráfico imaginario que en realidad no llegará
      Las ventas de cloud son hábiles para tocar las ansiedades de los inversionistas
    • Pensé en alojar un proyecto paralelo en el sótano, pero eso lo vuelve vulnerable a cortes de luz y fallas del ISP
      Si un disco falla mientras estoy de viaje, no hay mucho que hacer, y si me bloqueo por error, tampoco tengo una terminal serial de fallback
      Podría montarlo como la gente de r/homelab, pero entonces no queda claro si realmente ahorro dinero, sobre todo contando mi tiempo
      En conclusión, creo que los proveedores cloud son una oferta bastante buena gracias a las economías de escala
    • Al ver el costo de las VM, lo que lo encarece no es tanto mucho cómputo, sino necesitar servidores con mucho disco local
      Curiosamente, no se puede conseguir solo una de esas cosas por separado
      Imagina qué negocio o servicio tan grande podrías operar con 4 discos de 20 TB y una CPU razonable
      No es fácil conseguir una configuración así de un proveedor cloud
  • La parte de “la comunidad web temprana aprendió rápido que esto era una mala idea e inventó tecnologías como PHP” estrictamente hablando tenía como tecnología clave a mod_php
    PHP en sí no era distinto de Perl en su forma de ejecutarse, pero gracias a las decisiones de diseño de mod_php frente a mod_perl, los scripts PHP podían simplemente tirarse al servidor y ejecutarse rápido, mientras que hacer funcionar mod_perl requería algo de reflexión y magia

    • En esa época, con un amigo estábamos desarrollando algo que después se llamaría un sistema de gestión de aprendizaje
      Tenía gestión de contenidos, subida de tareas, calendario de eventos, gestión de calificaciones, chat en tiempo real y foros, y todo era C puro sobre CGI, lo cual era un infierno para trabajar
      El día que conocí PHP casi me dieron ganas de llorar, porque todo lo que hasta entonces escribíamos con esfuerzo desde cero leyendo RFC o haciendo ingeniería inversa de HTTP, en PHP era una simple llamada a función
      Ya no hacía falta depurar implementaciones torpes de urlencode ni perder un día por un retorno de carro raro en un encabezado HTTP
    • Es cierto, pero mod_php se agregó muy temprano al ecosistema PHP y rápidamente se volvió la forma de distribución por defecto
      Creo que la primera versión del módulo de Apache fue para PHP/FI Version 2.0, en 1996: https://www.php.net/manual/phpfi2.php#module
  • Si cgi-bin necesita acceder a la base de datos, debe abrir una conexión cada vez que se inicia el proceso
    Mantener el código en memoria, como FastCGI, no es solo para evitar el costo del tiempo de arranque, sino también porque permite tener un pool de conexiones a la BD o al menos conexiones persistentes por hilo

    • Cuando la escala crece, la base de datos sufre por la cantidad de conexiones
      Eso pasó al combinar “Python es de un solo hilo, así que levantemos muchos procesos” con “Python es lento, así que levantemos muchos procesos”
      Cuando la escala crece, al final hay que usar un pool de conexiones compartido fuera de Python, como pgbouncer, y mucho tuning para manejar la carga sin matar la BD
      Claro que después, al reimplementarlo en un lenguaje multihilo con cierto rendimiento, volvió a ser muy simple
    • Por eso CGI terminó evolucionando hacia un modelo que mantiene algo de estado entre solicitudes
    • También había una forma estándar de manejar esto
      En la práctica, consistía en levantar un demonio separado que actuaba como proxy
      Si se usan sockets Unix en lugar de TCP/IP, el costo de conexión es relativamente bajo
    • Basta con usar UDP
  • ¿No es un poco malo que en este hardware una app hello world haga 2,400 solicitudes por segundo?
    No sé exactamente qué se gana a cambio de sacrificar rendimiento. El código tampoco se volvió más simple

    • Es una mala cifra solo si necesitas superar las 2,000 solicitudes por segundo
      Esos sitios son apenas una parte del total
    • No es excelente, pero alcanza para muchos casos de uso
      Probablemente incluso aguante una avalancha de tráfico de HN
  • También se discutió ayer: https://news.ycombinator.com/item?id=44464272

  • En la empresa todavía servimos un directorio cgi-bin para apps web internas que a veces se hacen rápido
    Si lo mantienes simple, resulta cómodo de usar
    Que sea CGI no significa que tengas que imprimir HTTP/1.0 directamente a stdout
    Por ejemplo, si usas el wsgiref.handlers.CGIHandler integrado de Python, puedes ejecutar cualquier app WSGI como script CGI
    import wsgiref.handlers, flask
    app = flask.Flask(name)
    wsgiref.handlers.CGIHandler().run(app)
    Nosotros ejecutamos los scripts con uwsgi y su plugin CGI[1]
    Se siente más simple y flexible que levantar Apache o lighttpd por mod_cgi
    Como uwsgi se ejecuta como una unidad de systemd, también podemos aprovechar todas las funciones de endurecimiento y sandboxing de systemd
    Algo cómodo del manejo de CGI en uwsgi que no existe en mod_cgi es que puedes especificar el intérprete que se usará para ciertos tipos de archivo
    cgi = /cgi-bin=/webapps/cgi-bin/src
    cgi-allowed-ext = .py
    cgi-helper = .py=/webapps/cgi-bin/venv/bin/python3 # all dependencies go here
    El tiempo hasta el primer byte es de 250~350 ms, y para nuestro uso es aceptable
    [1]: https://uwsgi-docs.readthedocs.io/en/latest/CGI.html

    • Buena información; wsgiref.handlers.CGIHandler parece que todavía no está marcado para deprecación
  • Hace poco usé Apache para un proyecto paralelo, y una de las razones fue la funcionalidad .htaccess, así que tuve una conversación parecida
    Puedes poner archivos .htaccess en cualquier lugar y Apache puede leerlos como configuración adicional del servidor en cada solicitud: https://httpd.apache.org/docs/2.4/howto/htaccess.html
    Una gran razón para evitar esto era el rendimiento. Requería accesos adicionales a disco en cada solicitud, y siempre era mejor ponerlo en el archivo de configuración principal si era posible
    Pero hoy la mayoría de los servidores tienen SSD, y es muy probable que Linux también tenga RAM libre para usar en la caché del sistema de archivos
    Claro que el rendimiento sigue bajando un poco porque Apache tiene que parsear la configuración en cada solicitud, no una sola vez
    Aun así, considerando que hoy la mayoría de los CPU de servidor son más potentes, en muchos casos de uso es algo asumible
    El proyecto paralelo está en una versión muy inicial, pero ya se está usando: https://github.com/StaticPatch/StaticPatch/tree/main

    • Citando a Rasmus Lerdorf:
      “No soy un programador de verdad. Voy pegando cosas hasta que funcionen y luego sigo adelante. Los programadores de verdad dirían: ‘Bueno, funciona, pero hay fugas de memoria por todos lados. ¿No habría que arreglarlo?’. Yo simplemente reinicio Apache cada 10 solicitudes.”
      PHP ha avanzado muchísimo desde entonces, pero gran parte de eso fue corregir los errores iniciales
      “La razón por la que PHP 8 es mucho mejor es que contiene mucho menos de mi código.”
    • No entiendo por qué Apache no simplemente vigila el sistema de archivos
      Por esta decisión, el 99.99% de las solicitudes HTTP terminan ralentizadas por lecturas de disco innecesarias
  • Estuve pensando más en esto como parte de un flujo de trabajo de prototipado rápido
    Me parece que, en los lenguajes JIT modernos, el tiempo de arranque estará dominado en gran parte por los imports, a menos que vayan por el modelo FastCGI
    Se me ocurrió esto al empezar a usar el servidor web h2o para scripts locales; con mruby y handlers FastCGI se pueden escribir archivos de configuración limpios y rápidos, y además es muy veloz: https://h2o.examp1e.net/configure/fastcgi_directives.html
    Otro caso donde resulta útil es cuando se permite que los clientes extiendan software local con su propio código
    Por ejemplo, en vez de hacer que usen MCP para extender herramientas de IA, se les podría pedir que implementen cierta estructura de requests como CGI

    • Un frontend MCP para programas CGI en el entorno del usuario final no es una mala idea
      Me pregunto si el propio servicio MCP no podría implementarse como CGI
      Un framework MCP también podría exponer funcionalidades en forma de programa que admita ambos modos de ejecución
      Tendría que indagar un poco más en la especificación
    • FastCGI tiende a perder casi todas las ventajas de CGI
  • Para mí, inetd era básicamente CGI
    Gracias a eso, internet se volvió mucho más divertida
    Yo mismo alojé varios scripts de shell con inetd, e incluso tuve un servidor HTTP escrito completamente en Bash
    Ese VPS desapareció hace mucho, y en ese entonces ni siquiera usaba control de versiones
    La laptop en la que lo escribí también desapareció
    Pero fue realmente divertido
    El despliegue era tan fácil como Makefile + scp, y las pruebas eran otro script en Bash que combinaba varios netcat con grep
    Qué buenos tiempos