Que tus "futures" de larga duración no impidan que tu programa Python termine

En Python: Olvídate de los hilos y usa "futures" defiendo el uso de Futures en vez de hilos tradicionales por su mayor facilidad de uso, pero vemos que no existe una forma simple de marcar un Future como Daemon. Es decir, que el programa no terminará su ejecución hasta que todos los Futures se completen.

Esto no está mal. De hecho nos asegura que los recursos utilizados por los Futures se liberen de forma ordenada. Puede ser necesario borrar ficheros temporales o desconectarse de la base de datos de forma adecuada, por ejemplo. [1]

Con el modelo de actores que empleo habitualmente, los Futures no terminan nunca. Se pueden quedar ahí esperando eternamente a que alguien les pida algo. Por supuesto es posible definir un mensaje global que solicite un suicidio colectivo, pero ello implica la gestión de un mensaje adicional y el registro de todos los actores en algún sitio. Ninguna de las dos cosas es realmente un problema, pero en muchos casos no necesitamos estas complejidades.

Un ejemplo común es el caso de Futures de larga duración esperando trabajo mientras el hilo principal supervisa que no falle ninguno. Si alguno muere inesperadamente, el hilo principal lo detecta, notifica la excepción y el programa termina.

Existen dos posibilidades básicas, a priori:

Leer más…

Python: Olvídate de los hilos y usa "futures"

Soy un usuario masivo de hilos a la hora de programar en Python. Mi gestión de la complejidad y las condiciones de carrera se basa en ser cuidadoso y, cuando el problema lo permite, usar colas u otros mecanismos de sincronización y comunicación. Podría decirse que uso un modelo de actores en Python.

Con cuidado y experiencia me las apaño bastante bien. Es raro que tenga problemas.

Python 3.2 introdujo el concepto de Futures, un tipo de paralelismo que abstrae el uso de hilos o multiprocesamiento. En mi caso personal lo que más me interesa es la facilidad para seguir la pista a los diferentes hilos que lanzo, más concretamente para detectar de forma sencilla cuándo terminan correctamente o cuándo mueren debido a una excepción.

Antes de los futures, mis hilos estaban contenidos en algo similar a:

Leer más…

Cómo acceder al "code object" de una clase Python

Una de las características más interesantes de Python es su capacidad de introspección. Recientemente necesité acceder al code object de una clase Python y lo cierto es que no es tarea trivial.

Contexto: Creo una clase caché con persistencia transparente. Normalmente incluiría un número de versión en la persistencia y un mecanismo de migración para que pueda reutilizar los datos persistentes si modifico el código que interactúa con ella. Eso requiere estar atento y actualizar la versión y el proceso de migración cuando hay cambios relevantes en el código. Esto es manual. En este caso concreto no me interesa tener un mecanismo de migración de versiones con su mantenimiento manual. Dado que esto es una caché, si hay un cambio de código lo que quiero es que se detecte y se ignore la persistencia antigua.

He encontrado tres formas de acceder al code object de una clase Python:

Leer más…

Por qué es mala idea (a primera vista) desplegar IPv6 con 'Unique Local Address'

En Cómo prestar servicio IPv6 a través de OpenVPN explico cómo configurar OpenVPN para dar servicio IPv6. Los entendidos del tema se habrán sorprendido por el uso de NAT cuando estoy utilizando direcciones IPv6

La práctica habitual sería o bien no usar NAT en absoluto por ser innecesario o bien usar NAT para poder emplear internamente direcciones Unique Local Address.

En una primera versión de todo esto empleé direcciones Unique Local Address, concretamente fd00::/8. Esto me permitía entregar a cada uno de mis usuarios OpenVPN un rango ::/48. Estupendo.

Una vez montado, sin embargo, llega el jarro de agua fría:

Leer más…

Cómo prestar servicio IPv6 a través de OpenVPN

Cansado de que mi proveedor de Internet no me ofrezca IPv6 en mi red doméstica de forma nativa, decidí tomar cartas en el asunto.

Hace una década que navego con mi portátil y mi móvil a través de OpenVPN. Así no tengo que preocuparme de direcciones IP dinámicas o que la WIFI que esté utilizando sea una red hostil.

Las versiones recientes de OpenVPN soportan IPv6.

Veamos los cambios que realizo en mi configuración actual:

Leer más…

Parche de autenticación SMTP para Mailman 2

Increíblemente Mailman 2 no ha incluído autenticación SMTP hasta la versión 2.1.23 publicada el 27 de agosto de 2016. Y eso a pesar o, tal vez, a causa de que el parche es trivial y de que todo el mundo tenía su propia versión. Por ejemplo:

Yo también he tenido mi versión propia durante ocho años:

--- SMTPDirect.py.OLD   2017-11-24 00:38:36.601046958 +0100
+++ SMTPDirect.py       2017-11-24 00:38:07.104575066 +0100
@@ -62,6 +62,14 @@
     def __connect(self):
         self.__conn = smtplib.SMTP()
         self.__conn.connect(mm_cfg.SMTPHOST, mm_cfg.SMTPPORT)
+
+        # MI CODIGO - jcea@jcea.es - 20091016
+        if mm_cfg.SMTPTLS :
+            self.__conn.starttls()
+        if mm_cfg.SMTPUSER :
+            self.__conn.login(mm_cfg.SMTPUSER, mm_cfg.SMTPPASSWORD)
+        #
+
         self.__numsessions = mm_cfg.SMTP_MAX_SESSIONS_PER_CONNECTION

     def sendmail(self, envsender, recips, msgtext):

Me alegro de que ya no sea necesario.

Actualización 20171205: Tras ser un poco pesado (no mucho), he conseguido que pkgsrc actualice a la versión 2.1.25 de Mailman. Estará disponible en la distribución 2017Q4.

Uso de "nullmailer" en zonas nativas SmartOS

Las zonas nativas SmartOS incorporan un servidor de correo Postfix que hay que configurar correctamente para procesar los mensajes que puedan generarse de forma local como resultado de ejecuciones fallidas del demonio CRON o servicios similares. Enfrentado a este problema, lo que necesito exclusivamente es un sistema de "mail forwarding" que envíe todo el correo generado de forma local a mi servidor de correo normal, que reside en otra máquina diferente. No quiero inteligencia de ningún tipo, tan solo un simple "mail forwarding" ciego.

Se puede configurar Postfix para que haga "mail forwarding", de hecho es una configuración frecuente, pero me parece matar moscas a cañonazos. Preguntando sobre el particular en el canal IRC de SmartOS, muy amigable, se me sugirió un paquete disponible directamente en Pkgsrc: nullmailer.

Nullmailer hace exacta y exclusivamente lo que necesito: enviar a un servidor de correo remoto todo el correo que se genere de forma local.

No obstante, hay un problema: Nullmailer contiene un bug que hace que los mensajes generados por CRON, precisamente mi caso de uso, se reciban sin cabeceras; las cabeceras acaban como cuerpo del mensaje. Es decir, el mensaje que te llega es algo de este tipo:

From root Mon Nov 14 23:42 UTC 2016
To: root
Subject: Cron <root@5ca32ab7-57b7-4af4-8740-69fcd29cef86> echo hi
Auto-Submitted: auto-generated
X-Mailer: cron (SunOS 5.11)
X-Cron-User: root
X-Cron-Host: 5ca32ab7-57b7-4af4-8740-69fcd29cef86
X-Cron-Job-Name: echo hi
X-Cron-Job-Type: cron
MIME-Version: 1.0
Content-Type: text/plain
Content-Length: 3

hi

El problema es que a nullmailer no le gustan los mensajes cuya primera línea empieza por "From " o ">From ", aunque son legales.

La solución evidente sería parchear nullmailer, pero no quiero modificar paquetes binarios de pkgsrc. En vez de ello lo que he hecho es un wrapper en Python que filtra el mensaje entrante y simplemente se salta la primera línea si empieza por "From " o ">From ".

Leer más…

Using Mailman 2 in a machine without a mail server

Mailman is a mailing list software written in Python. It includes mailing list operation, management and a web interface.

Advertencia

The content of this article refers to Mailman 2. This won't probably work AS IS in Mailman 3. I haven't migrated yet.

Usually Mailman is running in a machine having a web server and a mail server. In this article I will describe how to operate a Mailman environment where the mail server resides in a different machine. Why? Because I already have a mail server running. Taking care of configuration, operation and spam filtering of an additional service is a burden.

Leer más…

Cómo analizar el uso del SWAP en un sistema Linux

En informática existe la regla básica de que todo debe caber en RAM. El espacio SWAP no debería utilizarse. La realidad es más complicada y depende del sistema operativo. En Solaris, por ejemplo, no hay una distinción clara entre RAM y SWAP y, de hecho, al medir el espacio disponible se muestra la suma de ambos recursos y cuando un proceso reserva memoria, se reserva de ese total. En Linux, en cambio, existe el concepto de overcommit de memoria: un proceso puede pedir la memoria que le de la gana, pero no se reservarán bloques de memoria hasta que el proceso los vaya utilizando. Esto permite que un proceso tenga un espacio de direcciones inmenso si no lo utiliza de forma intensiva, pero la contrapartida es que si un proceso realmente emplea toda la memoria que ha solicitado, puede fallar a mitad de ejecución porque no hay recursos suficientes. En Solaris fallaría directamente en la solicitud de memoria inicial.

Sobre el SWAP hay muchos mitos. Por ejemplo, es frecuente reservar tanto espacio de SWAP como RAM tengas en la máquina o el doble de dicha cantidad. ¿Por qué? La respuesta habitual es "porque sí, todo el mundo sabe que es lo que hay que hacer". Hay algunas razones históricas, pero hoy en día no tienen sentido.

La verdadera regla de oro respecto al SWAP es que todo el working set de los procesos en ejecución en el sistema debe caber en RAM. Si tu working set no cabe en RAM, tu rendimiento será ridículo. Una vez que tienes RAM suficiente, el tamaño del SWAP depende de tus necesidades. En Solaris, por ejemplo, es conveniente proporcionar un SWAP amplio porque el sistema lo usará para enviar allí lo que menos se use y así hacer hueco en RAM para cosas más importantes. En Linux el SWAP es más secundario, en buena parte debido al overcommit. Hay que señalar que los entornos Linux que no pueden permitirse que mueran procesos arbitrarios por escasez de recursos, se configuran para desactivar el overcommit y las necesidades de SWAP cambian completamente.

Leer más…

Upgrading SmartOS when installed in your harddisk

Advertencia

Installing SmartOS in your harddisk is a not supported configuration. Be careful and try to understand what are you doing.

SmartOS is designed to boot from USB, DVD or PXE. Nothing is installed in the harddisk, only your configuration and data. Upgrading the SmartOS hypervisor is, therefore, trivial and risk free and rollback safe.

In Installing/booting SmartOS in/from a harddisk without physical access I describe a procedure to install SmartOS in your harddisk when you have no physical access to your server. This is very important to me because I want to run SmartOS on hosted servers that I have never actually seen in real life.

To upgrade SmartOS in this configuration you can do this:

Leer más…