Cómo un finalizador de Python llenó /tmp de basura y tumbó la CI
El equipo de Flude tardó dos intentos en ver que el recolector de basura de Python no sirve para liberar disco: primero dejó carpetas huérfanas en /tmp y luego bloqueó la CI.

Un build nocturno del SDK en C++ falló con el diagnóstico de siempre: no queda espacio en disco. El directorio /tmp del runner de CI estaba lleno de miles de carpetas temporales con el mismo prefijo, ude_xml_, y todas las había creado el propio motor de documentación del equipo. El problema llevaba tiempo ahí; simplemente nadie lo había mirado.
El pipeline genera documentación a partir de código fuente: lanza Doxygen, Doxygen escupe cientos de megabytes de XML en un directorio creado con tempfile.mkdtemp() y después un cleanup() hace shutil.rmtree() sobre él. El diseño tenía un agujero. Si el parseo lanzaba una excepción, el pipeline abortaba antes de llegar a la limpieza y la carpeta se quedaba en /tmp para siempre. Con proyectos grandes, eso son cientos de megabytes por ejecución.
Delegar la limpieza en el recolector de basura
El primer intento de arreglo fue el que suele salir en las búsquedas: cambiar mkdtemp() por tempfile.TemporaryDirectory, cuyo finalizador borra la carpeta cuando el objeto desaparece de memoria. Guardaron una referencia en una lista global del gestor para que el objeto viviera exactamente lo que duraba el build. Durante unos días las quejas pararon.
Luego los runners de CI volvieron a caer. Los parsers del motor tienen referencias circulares —un nodo del AST apunta a su clase padre y la clase guarda la lista de hijos—, así que los objetos no se liberaban al terminar cada módulo. Se acumulaban en RAM, y con ellos la lista global. Cuando el recolector por fin se ponía a ello, borraba los archivos de golpe y de forma síncrona, dejando el hilo principal bloqueado varios minutos. El resultado fue peor que el original: otros servicios del servidor empezaron a fallar por timeouts.
La regla que adoptaron
Su conclusión es que usar finalizadores para liberar recursos pesados del sistema es un antipatrón, porque no controlas cuándo se ejecutan ni, por tanto, cuánta E/S de disco provocan. Reescribieron el ciclo de vida de las carpetas con gestores de contexto with: quien crea la carpeta es quien la limpia, en el mismo sitio, y todo acceso a esos archivos ocurre dentro del bloque. Si algo falla, la salida del bloque borra igual.
Añadieron además un bedel: antes de cada build se revisa /tmp y se fuerza la limpieza de cualquier carpeta huérfana que aparezca, dejando un aviso en el log. Es defensa en profundidad, no la solución.
El episodio deja una lección que aplica a cualquier lenguaje con recolección de basura: el GC gestiona memoria, no descriptores, sockets ni disco. Cualquier recurso del sistema que se libere con latencia indeterminada y de forma síncrona acaba mordiendo bajo carga. El equipo avisa de que la pelea con Doxygen no terminó ahí y les obligó a reescribir el parser entero; esa parte la cuentan aparte.
