“640 KB es suficiente”: El mito de la cita que no muere
En algún lugar de tu organización hay una celda de hoja de cálculo que asume en silencio “este límite no importará”.
Puede ser la profundidad de una cola, un heap JVM por defecto, un recuento de inodos, el tamaño de una tabla NAT, una caché de escritura diferida o un volumen “temporal” de 10 GB.
Nadie recuerda por qué es ese número. Todo el mundo lo trata como si fuera física.
Así es como acabas en una llamada de incidente a las 03:00, discutiendo con gráficas que parecen mentira.
La cita “640 KB es suficiente” sobrevive porque halaga nuestro peor hábito: creer que las limitaciones de hoy son permanentes y la demanda de mañana negociable.
El mito, por qué es pegajoso y qué nos enseña
La cita suele presentarse así: “640 KB ought to be enough for anybody.” Normalmente se atribuye a Bill Gates, situada en la era temprana del PC,
y se usa como remate sobre la arrogancia, la miopía o la velocidad del cambio tecnológico.
Hay un problema: no hay evidencia sólida de que él la dijera. La atribución es endeble, el periodo es difuso y la cita suele aparecer en impresos mucho después.
La historia persiste de todos modos porque es útil. Condensa una historia compleja de hardware, sistemas operativos y compromisos comerciales en una sola frase digna de burla.
A los ingenieros les encanta una moraleja clara. A los gerentes les encanta un villano claro. Y a todos les encanta una cita que puedes lanzar en una reunión como una granada de humo.
Pero los sistemas en producción no fallan porque alguien dijo algo tonto. Fallan porque existía un límite, se malentendió y luego se trató como una constante.
Aquí está el punto que debes mantener: 640 KB no fue una creencia sobre el futuro; fue un límite creado por decisiones de diseño y presión de compatibilidad.
El equivalente moderno no es “alguien pensó que la RAM no crecería”. Es “no sabemos qué límite es real, cuál es un valor por defecto y cuál es una mina”.
Primera broma corta: La cita “640 KB es suficiente” es como un ticket de incidente zombi—nadie sabe quién lo creó, pero sigue reabriéndose.
Hechos y contexto: qué fue realmente 640 KB
Para entender por qué este mito se adhiere a la cronología, necesitas los detalles aburridos. Los detalles aburridos son de donde vienen los fallos.
Aquí hay puntos de contexto concretos que importan, sin cosplay.
8 hechos que explican el límite de 640 KB (y por qué no fue aleatorio)
-
El IBM PC original usaba el Intel 8088, cuyo modelo de direccionamiento y la arquitectura temprana del PC hicieron que 1 MB de espacio de direcciones fuera un techo natural para esa era.
El “límite de 1 MB” no era una sensación; era estructural. -
La memoria convencional fue los primeros 640 KB (0x00000–0x9FFFF). Por encima residía espacio reservado para memoria de video, ROM y mapeos de hardware.
Esa región reservada es por qué “640 KB” aparece como un número claro. -
El área de memoria superior (UMA) existía por una razón: adaptadores de video, ROM del BIOS y ROMs de expansión necesitaban espacio de direcciones.
La compatibilidad con PC no era opcional; era el producto. -
MS-DOS se ejecutaba en real mode, lo que significaba que vivía en ese mundo de memoria convencional.
Puedes gritarle a la historia, pero la CPU sigue haciendo lo que la CPU hace. -
Expanded memory (EMS) y extended memory (XMS) fueron soluciones:
EMS conmutaba bancos de memoria en un marco de página; XMS usaba memoria por encima de 1 MB con un gestor. Ambos fueron impuestos de complejidad pagados por la compatibilidad. -
HIMEM.SYS y EMM386.EXE eran herramientas comunes para acceder y gestionar memoria más allá de los límites convencionales.
Si alguna vez “optimizaste” CONFIG.SYS y AUTOEXEC.BAT, estabas haciendo planificación de capacidad con un editor de texto y una oración. -
El protected mode existía, pero los ecosistemas de software se quedaron atrás.
La capacidad del hardware no reescribe el mundo instantáneamente; la base instalada y la matriz de compatibilidad deciden lo que puedes enviar. -
Esa era estuvo llena de restricciones estrictas, pero también de cambios rápidos.
La gente no era estúpida; construían sistemas donde cada kilobyte tenía un trabajo. El mito sobrevive porque confundimos restricción con arrogancia.
La conclusión útil: el número “640 KB” vino de un mapa de espacio de direcciones y decisiones de ingeniería pragmáticas, no de una declaración de que los usuarios nunca querrían más.
Es la diferencia entre “esta es la caja que podemos dibujar hoy” y “esta caja siempre será suficiente”.
La lección real: los límites son decisiones, no trivialidades
No me importa quién dijo qué en 1981. Me importa que en 2026 los equipos aún envían sistemas con techos invisibles y luego se sorprenden cuando los alcanzan.
La historia de “640 KB” es un espejo: nos muestra lo que actualmente estamos dejando pasar con un gesto.
Cómo se ve “640 KB” en la producción moderna
- Cupos por defecto (almacenamiento efímero de Kubernetes, tamaños de volúmenes en la nube, límites por namespace) tratados como si fuesen política.
- Valores por defecto del kernel (somaxconn, nf_conntrack_max, fs.file-max) sin tocar porque “Linux sabe mejor”.
- Límites del sistema de archivos (inodos, comportamiento de escalado de directorios, sobrecarga de archivos pequeños) ignorados hasta que “df dice que hay espacio”.
- Suposiciones de caché (“más caché siempre es más rápido”) que se convierten en presión de memoria, tormentas de expulsión y picos de latencia en la cola.
- Encolamiento y retropresión que no existen, porque alguien quiso “simplicidad”.
Una cita es reconfortante; un inventario de límites es útil
El mito prospera porque te da un villano. Los villanos son fáciles. Los límites requieren trabajo.
Si operas sistemas en producción, tu trabajo es conocer los límites antes de que tus usuarios lo hagan.
Aquí hay una idea parafraseada de una voz notable en confiabilidad, porque es lo opuesto del mito de 640 KB:
paraphrased idea — John Allspaw: La confiabilidad proviene de aprender y adaptar sistemas, no de culpar a individuos por los resultados.
Trata “640 KB es suficiente” como un disparador de diagnóstico: ¿dónde confiamos en un artefacto histórico, un valor por defecto o una restricción medio-recordada?
Luego ve a encontrarlo. Escríbelo. Pruébalo. Pon alertas sobre ello. Hazlo aburrido.
Tres mini-historias corporativas del país del “estará bien”
Mini-historia 1: Un incidente causado por una suposición errónea (“no puede pasar que el disco se llene; tenemos monitoreo”)
Una empresa SaaS mediana ejecutaba un clúster Postgres multi-tenant con replicación lógica hacia un sistema de reporting.
La BD primaria tenía mucho espacio libre, y los paneles mostraban “uso de disco estable”. Todos dormían tranquilos.
Una noche, las escrituras se ralentizaron, luego se detuvieron. Las tasas de error de la aplicación subieron. El on-call vio que la BD estaba “saludable” según sus comprobaciones habituales:
CPU bien, RAM bien, lag de replicación en aumento pero no catastrófico. El clúster no se cayó; simplemente dejó de avanzar de forma segura y parecía melaza.
Causa raíz: el volumen de WAL se llenó. No el volumen de datos principal. La partición de WAL tenía un tamaño diferente, comportamiento de crecimiento distinto y un umbral de alerta distinto.
El tablero de “uso de disco estable” miraba el sistema de archivos de datos. Nunca miró la partición de WAL, porque alguien asumió “está en el mismo disco”.
Peor: el proceso de limpieza que debería haber eliminado segmentos WAL antiguos dependía de replication slots. Un consumidor atascado mantenía los slots abiertos.
Así que WAL creció hasta alcanzar el límite de la partición. La base de datos hizo exactamente lo que debía cuando no puede persistir de forma segura: dejó de aceptar trabajo.
La solución fue sencilla—redimensionar la partición, añadir alertas, destrabar al consumidor y establecer políticas sensatas de retención. La lección incómoda no lo fue.
El equipo no había pasado por alto un modo de fallo complejo. Habían olvidado un ítem básico del inventario: qué volúmenes existen, qué los llena y con qué rapidez.
Mini-historia 2: Una optimización que salió mal (“usaremos cachés grandes; la memoria es barata”)
Un servicio de pagos tenía problemas de latencia durante tráfico pico. El equipo optimizó: más caché en proceso, pools de conexión mayores
y cachés read-through agresivos para metadatos accedidos frecuentemente. La latencia mejoró en staging. El deploy salió con confianza.
En producción, la latencia de cola mejoró unas horas. Luego las cosas se pusieron raras. P99 subió, el uso de CPU se disparó y las tasas de error fueron intermitentes.
El servicio no parecía sobrecargado—hasta que comprobaste major page faults y actividad de reclaim. El kernel estaba luchando por sobrevivir.
La optimización creó presión de memoria y forzó al kernel a reclamar agresivamente la caché de archivos. Eso significó más lecturas de disco para dependencias.
También empujó a la JVM (sí, esto era Java) a una postura de GC que parecía una sierra de arrepentimiento.
El servicio se había vuelto “rápido de media” e “impredecible cuando importaba”, que es el peor tipo de rápido.
Revirtieron los tamaños de caché, añadieron un presupuesto explícito de memoria y movieron parte de la responsabilidad de caché a una capa dedicada que se podía escalar por separado.
La solución a largo plazo incluyó SLOs por endpoint y pruebas de carga que modelaron la cardinalidad pico y el churn de caché—no solo QPS en estado estable.
La lección: “la memoria es barata” no es un argumento de ingeniería. La memoria es un recurso compartido que interactúa con IO, GC y planificación.
Las cachés no son gratis; son préstamos que devuelves con impredecibilidad a menos que las presupuestes.
Mini-historia 3: Una práctica aburrida pero correcta que salvó el día (margen de capacidad + simulacros de límites)
Un equipo de plataforma interna empresarial ejecutaba gateways de almacenamiento de objetos frente a un gran backend de almacenamiento.
El sistema servía logs, artefactos y backups—todo aquello en lo que nadie piensa hasta que desaparece.
El equipo tenía una práctica poco sexy: cada trimestre ejecutaban un “simulacro de límites”.
Elegían una restricción—descriptores de archivo, conexiones de red, tamaño de caché, rendimiento de disco, uso de inodos—y verificaban alertas, paneles y runbooks.
No lo hacían porque fuera divertido. Lo hacían porque los límites desconocidos son donde nacen los incidentes.
Una semana, un equipo de aplicación comenzó a subir millones de objetos diminutos debido a un cambio en el empaquetado.
El backend no estaba lleno en bytes, pero la presión de metadatos se disparó. Los nodos gateway empezaron a mostrar IO wait elevado y latencia incrementada.
El equipo de plataforma lo detectó temprano porque tenían alertas no solo en “porcentaje de disco usado” sino también en consumo de inodos,
profundidad de cola de solicitudes y latencia por dispositivo. Ralentizaron la carga ruidosa, coordinaron una corrección de empaquetado y añadieron una política de tamaño mínimo de objetos.
Nadie fuera del equipo de plataforma lo notó. Así es como “se salva el día”: no pasa nada y no recibes aplausos.
Segunda broma corta: La ingeniería de confiabilidad consiste en estar orgulloso de un incidente que nunca entra en una presentación.
Tareas prácticas: comandos, salidas, decisiones
Mi sesgo: si no puedes interrogar el sistema con un comando, no entiendes el sistema.
A continuación hay tareas prácticas que puedes ejecutar en un host Linux. Cada una incluye qué significa la salida y la decisión que tomas a partir de ella.
No son académicas; son las comprobaciones que haces cuando “algo se siente lento” y necesitas dejar de adivinar.
Tarea 1: Comprobar la presión de memoria y la realidad del swap
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 31Gi 24Gi 1.2Gi 512Mi 5.8Gi 3.9Gi
Swap: 2.0Gi 1.6Gi 400Mi
Significado: “available” es lo importante; refleja cache recuperable. Uso intensivo de swap sugiere presión de memoria sostenida, no un pico breve.
Decisión: Si el swap se usa activamente y la latencia es mala, reduces huella de memoria (presupuestos de caché, heap de JVM, número de workers)
o añades memoria. No trates el swap como “RAM extra”; trátalo como “seguro de latencia con una prima muy cara.”
Tarea 2: Identificar los mayores consumidores de memoria (y si es anónimo o caché de archivos)
cr0x@server:~$ ps -eo pid,comm,rss,vsz --sort=-rss | head
PID COMMAND RSS VSZ
4121 java 9876540 12582912
2330 postgres 2456780 3145728
1902 prometheus 1024000 2048000
1187 nginx 256000 512000
Significado: RSS muestra memoria residente; VSZ puede ser engañosa (espacio de direcciones reservado).
Un único proceso con RSS en crecimiento es un objetivo obvio.
Decisión: Si el crecimiento de RSS correlaciona con picos de latencia, aplica un presupuesto de memoria: capar cachés, tunear heap o aislar la carga.
Tarea 3: Ver si el kernel está reclamando agresivamente
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 1638400 120000 80000 5200000 10 25 120 300 1200 1800 20 8 60 12 0
4 1 1639000 90000 70000 5100000 80 120 400 1500 1600 2200 18 10 45 27 0
3 1 1639500 85000 65000 5000000 60 90 350 1200 1500 2100 15 9 50 26 0
2 0 1640000 110000 70000 5050000 15 30 150 500 1300 1900 19 8 58 15 0
2 0 1640000 115000 72000 5080000 5 10 100 320 1250 1850 21 7 62 10 0
Significado: Valores no cero en si/so (swap in/out) y alto wa (IO wait) indican que la presión de memoria se convierte en dolor IO.
Decisión: Si ocurre swapping durante picos, deja de “optimizar” en otros lados. Arregla la presión de memoria primero o perseguirás cuellos de botella fantasma.
Tarea 4: Comprobar el promedio de carga frente a la saturación de CPU
cr0x@server:~$ uptime
14:22:10 up 37 days, 3:11, 2 users, load average: 18.42, 17.90, 16.77
Significado: El promedio de carga incluye tareas ejecutables e ininterrumpibles (a menudo IO wait). Carga alta no significa automáticamente que la CPU esté saturada.
Decisión: Combina esto con comprobaciones de CPU e IO antes de declarar “necesitamos más núcleos.”
Tarea 5: Confirmar si la CPU es realmente el cuello de botella
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 01/21/2026 _x86_64_ (16 CPU)
12:00:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:00:02 AM all 22.11 0.00 7.90 0.80 0.00 0.60 0.00 68.59
12:00:03 AM all 24.01 0.00 8.10 9.50 0.00 0.70 0.00 57.69
12:00:04 AM all 21.20 0.00 7.50 11.40 0.00 0.60 0.00 58.70
Significado: %iowait está subiendo; la CPU no está saturada. El sistema está esperando almacenamiento/red.
Decisión: No escales CPU. Investiga latencia de IO, contención del sistema de archivos o comportamiento del almacenamiento en red.
Tarea 6: Chequeo rápido de espacio en disco (bytes) y de inodos (metadatos)
cr0x@server:~$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 900G 720G 180G 81% /
/dev/nvme1n1p1 200G 198G 2.0G 99% /var/lib/postgresql/wal
cr0x@server:~$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2 58982400 1048576 57933824 2% /
/dev/nvme1n1p1 13107200 13090000 17200 100% /var/lib/postgresql/wal
Significado: Un sistema de archivos puede “no estar lleno” en bytes pero sí en inodos. La partición de WAL está llena en ambos aspectos.
Decisión: Si el uso de inodos es el problema, borrar archivos grandes no ayudará. Necesitas eliminar muchos archivos pequeños o reconstruir con más inodos.
Tarea 7: Medir latencia y utilización de almacenamiento en vivo
cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server) 01/21/2026 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
20.15 0.00 7.30 9.42 0.00 63.13
Device r/s w/s rkB/s wkB/s await %util
nvme0n1 120.0 200.0 4096.0 8192.0 6.20 78.00
nvme1n1 10.0 900.0 512.0 16384.0 45.30 99.20
Significado: nvme1n1 está al máximo (%util ~99) con await alto. Eso es un cuello de botella.
Decisión: Mueve cargas de trabajo con muchas escrituras (WAL, logs) fuera de ese dispositivo, aumenta la capacidad del dispositivo o reduce la amplificación de escrituras.
Tarea 8: Encontrar qué procesos están haciendo IO ahora mismo
cr0x@server:~$ sudo iotop -o -b -n 3
Total DISK READ: 5.12 M/s | Total DISK WRITE: 42.33 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
2330 be/4 postgres 0.00 B/s 28.10 M/s 0.00 % 35.20 % postgres: wal writer
4121 be/4 cr0x 1.20 M/s 4.10 M/s 0.00 % 5.10 % java -jar service.jar
3011 be/4 root 0.00 B/s 3.20 M/s 0.00 % 2.00 % journald
Significado: El writer de WAL domina las escrituras. Esto no es “IO misterioso”; es tu base de datos haciendo su trabajo.
Decisión: Si el dispositivo de IO no puede seguir el ritmo, tunear consultas de la app no lo arreglará rápido. Atiende la ruta de almacenamiento primero.
Tarea 9: Revisar opciones de montaje de sistemas de archivos que te pueden morder
cr0x@server:~$ mount | grep -E ' / |wal'
/dev/nvme0n1p2 on / type ext4 (rw,relatime,errors=remount-ro)
/dev/nvme1n1p1 on /var/lib/postgresql/wal type ext4 (rw,relatime,data=ordered)
Significado: Buscas sorpresas: montajes sync, noatime/relatime, barreras, opciones extrañas que cambian patrones de escritura.
Decisión: Si encuentras sync o un sistema de archivos de red inesperado bajo una ruta sensible a latencia, ese es probablemente tu momento “640 KB”.
Tarea 10: Comprobar límites de descriptores de archivos (el “conventional memory” moderno de sockets)
cr0x@server:~$ ulimit -n
1024
cr0x@server:~$ cat /proc/sys/fs/file-nr
42112 0 9223372036854775807
Significado: El límite por proceso es 1024, lo cual es pequeño para muchos servicios. Los handles a nivel sistema están bien.
Decisión: Si ves errores de “too many open files” o churn de conexiones, sube límites por servicio vía systemd y verifica con un reinicio.
Tarea 11: Comprobar backlog de red y manejo de SYN (límites de cola que parecen “pérdida de paquetes aleatoria”)
cr0x@server:~$ sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog
net.core.somaxconn = 128
net.ipv4.tcp_max_syn_backlog = 256
Significado: Estos valores por defecto pueden ser demasiado bajos para servicios de alta concurrencia, causando drops de conexión bajo ráfagas.
Decisión: Si ves drops de SYN o desbordamiento de la cola de accept en métricas, tunea estos y realiza pruebas de carga. No “añadas pods” y esperes.
Tarea 12: Comprobar uso de la tabla conntrack (NAT y seguimiento de estado: el techo oculto)
cr0x@server:~$ sysctl net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_max = 262144
cr0x@server:~$ cat /proc/sys/net/netfilter/nf_conntrack_count
261900
Significado: Estás cerca del máximo. Cuando esto se llena, las nuevas conexiones fallan de formas que parecen errores de aplicación.
Decisión: Aumenta la tabla (con atención a la memoria), reduce churn de conexiones innecesarias y establece alertas en umbrales sensatos.
Tarea 13: Revisar logs del kernel por la verdad que no querías
cr0x@server:~$ dmesg -T | tail -n 8
[Mon Jan 21 13:58:11 2026] Out of memory: Killed process 4121 (java) total-vm:12582912kB, anon-rss:9876540kB, file-rss:10240kB, shmem-rss:0kB
[Mon Jan 21 13:58:11 2026] oom_reaper: reaped process 4121 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
[Mon Jan 21 14:00:02 2026] EXT4-fs warning (device nvme1n1p1): ext4_dx_add_entry: Directory index full, reached max htree level
Significado: OOM kills y advertencias del sistema de archivos no son “ruido”. El sistema te está diciendo que tus suposiciones son incorrectas.
Decisión: Si ves OOM, deja de añadir funcionalidades y empieza a dimensionar memoria. Si ves advertencias del índice del sistema de archivos, examina la disposición de directorios/archivos.
Tarea 14: Medir explosión de directorios y archivos pequeños
cr0x@server:~$ sudo find /var/lib/postgresql/wal -type f | wc -l
12983456
Significado: Millones de archivos implican presión de inodos, problemas de escalado de directorios y dolor en backups/escanes.
Decisión: Re-arquitecta el layout de archivos, rota agresivamente o muévete a un diseño que no use el sistema de archivos como base de datos.
Tarea 15: Confirmar si la app está restringida por cgroups (un tipo de “640 KB” muy 2026)
cr0x@server:~$ cat /sys/fs/cgroup/memory.max
2147483648
cr0x@server:~$ cat /sys/fs/cgroup/memory.current
2130014208
Significado: La carga está básicamente en su límite de memoria. Puedes tunear todo el día; el muro es literal.
Decisión: Aumenta el límite o reduce el uso de memoria. Además: pon alertas en memory.current acercándose a memory.max, no después de un OOM.
Guion de diagnóstico rápido: encuentra el cuello de botella velozmente
Cuando todo está lento, no tienes tiempo para filosofar sobre los 80. Necesitas una secuencia disciplinada que converja.
Este guion asume que un único host o nodo se comporta mal; adáptalo para sistemas distribuidos muestreando varios nodos.
Primero: confirma el modo de fallo (síntomas, no teorías)
- ¿Es latencia, rendimiento o errores?
- ¿Es degradación sostenida o picos espasmódicos?
- ¿Se correlaciona con despliegues, tráfico, cron jobs, ventanas batch?
Ejecuta: comprobaciones rápidas de load + CPU + memoria + IO. No adivines qué subsistema es el culpable.
Segundo: comprueba los cuatro cuellos de botella habituales en orden
-
Presión de memoria:
free -h,vmstat, límites de cgroup, logs de OOM.
Si hay swapping o OOM, trátalo como primario hasta que se demuestre lo contrario. -
Latencia de almacenamiento:
iostat -xz,iotop, llenado de sistemas de archivos y de inodos.
Await alto o %util cerca de 100% es una pistola humeante. -
Saturación de CPU:
mpstaty CPU por proceso.
Alto %usr/%sys con iowait bajo apunta a CPU. -
Red y colas: ajustes de backlog, conntrack, retransmisiones, drops.
Una tabla conntrack llena puede hacer que un servicio sano parezca poseído.
Tercero: localiza el impacto antes de “arreglar”
- ¿Qué proceso es top CPU / top RSS / top IO?
- ¿Qué montaje se está llenando?
- ¿Qué dispositivo tiene alta latencia?
- ¿Qué límite está cerca del techo (fds, conntrack, cgroups, disco, inodos)?
Cuarto: elige la mitigación menos arriesgada
- Reduce la velocidad del culpable (rate limits, pausa de jobs batch).
- Añade margen (aumenta tamaño de volúmenes, sube límites) si es seguro y reversible.
- Mueve caminos calientes fuera de recursos contenciosos (separa WAL/logs, aisla cachés).
- Revierte cambios recientes si la línea temporal encaja.
Quinto: haz que no vuelva a ocurrir
- Añade una alerta sobre la restricción real que alcanzaste (no una métrica proxy).
- Escribe un runbook que comience con “muéstrame el límite y el uso actual”.
- Programa un simulacro de límites. Ponlo en el calendario como parcheo. Porque lo es: parcheo de tus suposiciones.
Errores comunes: síntoma → causa raíz → solución
Aquí es donde el mito de 640 KB gana sentido. El fallo no es “no predecimos el futuro”.
El fallo es “no identificamos un límite y lo tratamos como una dependencia de producción”.
1) Síntoma: “El disco está 70% libre pero las escrituras fallan”
Causa raíz: agotamiento de inodos, límites de metadatos del sistema de archivos o una partición diferente (WAL/logs) está llena.
Solución: comprueba df -i y puntos de montaje; mueve caminos calientes a volúmenes dedicados; reconstruye sistemas de archivos con densidad de inodos adecuada si es necesario.
2) Síntoma: “El load average es enorme; los gráficos de CPU parecen bien”
Causa raíz: IO wait o tareas bloqueadas (latencia de almacenamiento, fallos de NFS).
Solución: ejecuta iostat -xz y iotop; investiga await y %util del dispositivo; arregla el cuello de botella de almacenamiento antes de escalar CPU.
3) Síntoma: “Timeouts aleatorios en ráfagas; añadir pods no ayuda”
Causa raíz: desbordamiento de la cola de accept, somaxconn bajo, backlog de SYN agotado o conntrack lleno.
Solución: tunea parámetros de backlog, aumenta conntrack max con sensibilidad a memoria y reduce churn de conexiones con keep-alives/pooling.
4) Síntoma: “La latencia mejoró tras cachear, luego empeoró más que antes”
Causa raíz: presión de memoria inducida por caché causando reclaim, swap o thrash de GC.
Solución: aplica presupuestos de caché; monitoriza page faults y reclaim; mueve cachés a capas dedicadas; prueba con cardinalidad y churn realistas.
5) Síntoma: “Reiniciar el servicio lo arregla por un rato”
Causa raíz: fuga de recursos (fds, memoria, conntrack), fragmentación o colas sin límite.
Solución: monitoriza crecimiento con el tiempo; fija límites duros; añade detección de leaks; implementa retropresión; no aceptes “reiniciar es el runbook”.
6) Síntoma: “La base de datos está lenta pero la CPU está baja”
Causa raíz: latencia de almacenamiento, contención de fsync, WAL en dispositivo saturado o ráfagas de checkpoint.
Solución: separa WAL en almacenamiento rápido, tunea checkpoints con cuidado, mide latencia de fsync y vigila la amplificación de escrituras.
7) Síntoma: “Plenty of RAM free; still OOM-killed”
Causa raíz: límites de cgroup, techos por contenedor o RSS anónimo alto bajo un tope rígido.
Solución: comprueba /sys/fs/cgroup/memory.max; aumenta límites; reduce memoria; asegura que las alertas se basen en uso de cgroup, no en free del host.
Listas de verificación / plan paso a paso
Checklist 1: Construir un “inventario de límites” para cualquier servicio importante
- Lista todos los montajes de almacenamiento usados por el servicio (data, logs, WAL, tmp, cache).
- Para cada montaje: registra tamaño, recuento de inodos, factores de crecimiento y mecanismo de limpieza.
- Registra techos de cómputo: límites de CPU, límites de memoria, tamaño de heap, pools de hilos.
- Registra techos del SO: valores de ulimit, límites de systemd, tamaño de conntrack, ajustes de backlog.
- Registra techos upstream: límites de conexiones BD, límites de tasa de API, cuotas de colas.
- Para cada techo: define un umbral de advertencia y un umbral de emergencia.
- Crea un único dashboard que muestre “uso actual vs límite” para todos los anteriores.
Checklist 2: Planificación de capacidad que no pretende ser profecía
- Mide el pico actual (no la media) para CPU, memoria, IO, red y crecimiento de almacenamiento.
- Identifica el primer recurso que alcanza 80% durante el pico; ese es tu primer objetivo de escalado.
- Define política de margen (ejemplo: mantener >30% de espacio libre en volúmenes calientes; mantener conntrack <70%).
- Modela el crecimiento como rangos, no líneas únicas. Incluye estacionalidad y jobs batch.
- Prueba modos de fallo: simula disco lleno, tabla de inodos llena, conntrack cerca del máximo, límites bajos de fd.
- Escribe qué significa “degradado pero aceptable” y cómo lo vas a hacer cumplir (throttling, shedding).
Checklist 3: Guardarraíles pre-despliegue (la rutina anti-640 KB)
- Antes de enviar un cambio de “rendimiento”, define el presupuesto de recursos que consumirá.
- Prueba de carga con cardinalidad pico, no con tráfico sintético uniforme.
- Verifica que existan alertas para el nuevo punto de presión real (memory.current, iowait, disk await).
- Asegura que el rollback sea viable y rápido.
- Ejecuta un canario lo suficientemente grande para golpear caches y colas reales.
Preguntas frecuentes
1) ¿Bill Gates realmente dijo “640 KB es suficiente para cualquiera”?
No hay evidencia primaria fiable. La cita se considera ampliamente mal atribuida o al menos no verificada.
Trátala como folclore, no como historia.
2) Si la cita es dudosa, ¿por qué hablar de ella en absoluto?
Porque es un proxy perfecto para un modo real de fallo: equipos que confunden un límite de diseño o un ajuste por defecto con una verdad permanente.
El mito es molesto; la lección es valiosa.
3) ¿Qué exactamente fue el límite “640 KB”?
Memoria convencional en la arquitectura IBM PC: la RAM utilizable por debajo del área reservada superior en el primer espacio de direcciones de 1 MB.
Las necesidades de mapeo de hardware (video, ROM) consumían el resto.
4) ¿Por qué no “usaron más de 1 MB”?
Sistemas posteriores sí lo hicieron, pero la compatibilidad importaba. El software temprano, las suposiciones de DOS en real mode y el ecosistema hicieron que los workarounds (EMS/XMS) fueran más prácticos que romperlo todo.
5) ¿Cuál es el equivalente moderno de la barrera de 640 KB?
Cualquier techo oculto: límites de memoria de contenedores, tablas conntrack, topes de descriptores de archivos, profundidades de cola, agotamiento de inodos, volúmenes por defecto pequeños o dispositivos de almacenamiento saturados.
La “barrera” está donde tu sistema choca con un límite rígido que no modelaste.
6) ¿No es esto simplemente “siempre planificar para el crecimiento”?
No exactamente. “Planificar para el crecimiento” se convierte en un gesto vacío. El trabajo real es: identificar límites específicos, seguir el uso contra ellos y ensayar qué ocurre al 80/90/100%.
7) ¿Deberíamos siempre subir límites preventivamente?
No. Subir límites a ciegas puede mover el fallo a otro sitio o aumentar el radio de impacto. Sube límites cuando entiendas el coste del recurso y tengas monitoreo y retropresión.
8) ¿Cómo evito que las optimizaciones de rendimiento salgan mal?
Presupuéstalas. Cada optimización consume algo: memoria, IO, CPU, complejidad o riesgo operativo.
Exige una “factura de recursos” en las revisiones y prueba bajo patrones de pico realistas.
9) ¿Y si no tenemos tiempo para un esfuerzo completo de planificación de capacidad?
Haz primero un inventario de límites. Es barato y útil de inmediato. La mayoría de los outages no vienen de unknown unknowns; vienen de límites conocidos que nadie documentó.
10) ¿Cuál es una métrica que añadirías en todos lados mañana?
“Uso vs límite” para cada techo crítico: bytes de disco, inodos, memory.current vs memory.max, fds abiertos vs ulimit, conntrack_count vs nf_conntrack_max.
Los porcentajes solos mienten; necesitas ver el techo.
Próximos pasos que puedes hacer esta semana
Deja de discutir si alguien dijo una frase en los 80. Tu sistema de producción está ocupado creando su propia cita.
La solución no es cinismo; es instrumentación y disciplina.
- Escribe tus 10 límites duros principales por servicio (memoria, disco, inodos, fds, conntrack, profundidades de cola, conexiones BD).
- Añade alertas en los techos reales, no en proxies. Alerta cuando te acerques al muro, no cuando ya estés sangrando.
- Ejecuta un simulacro de límites: elige una restricción y verifica que puedes detectarla, mitigarla y evitar recurrencias.
- Presupuesta cachés explícitamente. Si una caché no tiene tamaño máximo, no es una caché; es un incidente en cámara lenta.
- Separa caminos de IO calientes (logs/WAL/tmp) para que un vecino ruidoso no derribe tu almacenamiento crítico.
El mito de 640 KB no muere porque es memorable. Haz que tus límites también sean memorables—poniéndolos en dashboards y runbooks, donde pertenecen.