Member of The Internet Defense League Últimos cambios
Últimos Cambios
Blog personal: El hilo del laberinto Geocaching

Prioridades y Envíos Diferidos de Email (III)

Última Actualización: 18 de octubre de 2005 - Martes

Los parches anteriores han estado funcionando a la perfección durante años. Pero la experiencia ha puesto en evidencia los siguientes problemas:

  • Aunque la programación de envíos nocturnos permite un mejor uso de los recursos, resulta muy limitada. Por ejemplo, no se puede programar un envío diferido para -aproximadamente- una hora concreta. Tampoco se puede programar un envío diferido para dentro de varios días.

  • Cuando la cola de salida de correo se llena, hay que hacer sitio. En esos casos es muy fácil meter la pata y activar el envío de un mensaje diferido por error, ya que la activación manual de un mensaje en la cola tiene prioridad sobre su carácter de envío diferido.

  • El envío diferido no afecta a los usuarios locales. Es decir, si uno de los destinatarios es usuario de un dominio de correo hospedado en la misma organización, recibirá los mensajes al momento, independientemente de que se hayan programado como diferidos.

Estos problemas están causados, en realidad, por un abuso del sistema. La tecnología de envío diferido se diseñó como un medio para garantizar la calidad de servicio en las horas puntas, relegando el tráfico menos prioritario y menos sensible al retardo (el correo) a horas de baja utilización. Que se haya venido usando esta tecnología para programar explícitamente el envío de mensajes cuyo tiempo de llegada sea importante y delicado es un efecto secundario no intencionado.

Pero nos consta que el servicio de envío diferido es útil y está siendo utilizado de forma activa por parte de nuestros usuarios, por lo que nos hemos decidio a implementar un servicio de envío diferido "de verdad".

Más información en un boletín enviado a los usuarios de Argo:

El siguiente parche incluye a los anteriores, pero proporcionando una solución a los problemas descritos:

Index: sendmail/deliver.c
===================================================================
--- sendmail/deliver.c  (.../tags/sendmail-8.13.5)      (revision 391)
+++ sendmail/deliver.c  (.../branches/sendmail-8.13.5-jcea)     (revision 391)
@@ -68,6 +68,13 @@
        ENVELOPE *e;
        int mode;
 {
+struct h {
+  char *host;
+  struct h *siguiente;
+} *p=NULL,*p2;
+unsigned long tamanho_por_receptores;
+HDR *cabecera;
+
        register ADDRESS *q;
        char *owner;
        int otherowners;
@@ -78,6 +85,97 @@
        bool somedeliveries = false, expensive = false;
        pid_t pid;

+
+/*
+** jcea@argo.es - 18/May/99
+**
+** Manda los mensajes largos por la noche
+*/
+      char hora[20];
+      time_t tiempo,tiempo2;
+      int envio_diferido=0;
+
+
+#define TAMANO_MAXIMO 2000000
+
+/*
+** jcea@argo.es - 24/may/05
+**
+** Los mensajes marcados como de envio "diferido" deben procesarse apropiadamente
+*/
+        tiempo=time(NULL);
+        cftime(hora,"%H",&tiempo);
+
+        for (q = e->e_sendqueue; q != NULL; q = q->q_next) {
+          if((strlen(q->q_user)==19) && (!strcasecmp("@envio_diferido",q->q_user+4)) &&
+             ((q->q_user[0]>='0') && (q->q_user[0]<='9')) &&
+             ((q->q_user[1]>='0') && (q->q_user[1]<='9')) &&
+             ((q->q_user[2]>='0') && (q->q_user[2]<='5')) &&
+             ((q->q_user[3]>='0') && (q->q_user[3]<='9'))) {
+            tiempo2=(e->e_ctime)+(q->q_user[0]-'0')*36000+(q->q_user[1]-'0')*3600+(q->q_user[2]-'0')*600+(q->q_user[3]-'0')*60;
+            if(tiempo<tiempo2) envio_diferido=1;
+            goto salida_envio_diferido;
+          }
+        }
+        salida_envio_diferido:
+
+        tamanho_por_receptores=0;
+        if(((strcmp(hora,"02")<0) || (strcmp(hora,"07")>0)) &&
+        (QueueLimitRecipient==NULL) &&
+        (QueueLimitSender==NULL) &&
+        (QueueLimitId==NULL)) {
+          for (q = e->e_sendqueue; q != NULL; q = q->q_next) {
+            if(!bitnset(M_LOCALMAILER,q->q_mailer->m_flags)) {
+              if(q->q_host!=NULL) {
+                p2=p;
+                while(p2!=NULL) {
+                  if(!strcasecmp(p2->host,q->q_host)) goto siguiente_pasada;
+                  p2=p2->siguiente;
+                }
+                p2=malloc(sizeof(struct h));
+                if(p2!=NULL) {
+                  p2->siguiente=p;
+                  p=p2;
+                  p->host=q->q_host;
+                }
+              }
+              tamanho_por_receptores+=e->e_msgsize;
+            }
+          siguiente_pasada: ; /* Para evitar error de compilacion en GCC 3.4 */
+          }
+        }
+
+        while(p!=NULL) {
+          p2=p->siguiente;
+          free(p);
+          p=p2;
+        }
+
+/*
+** jcea@argo.es - 17/Jun/99
+**
+** Si el mensaje es de baja prioridad, lo manda de noche.
+*/
+        for(cabecera = e->e_header; cabecera != NULL; cabecera = cabecera->h_link) {
+          /* Si tiene prioridad CINCO, lo larga de noche */
+          if(strcasecmp(cabecera->h_field,"x-priority")==0) {
+            if(atoi(cabecera->h_value)==5) tamanho_por_receptores+=TAMANO_MAXIMO;
+          }
+        }
+
+/*
+** jcea@argo.es - 17/Jun/99
+**
+** Si el mensaje tiene alta prioridad, lo larga ya
+*/
+        for(cabecera = e->e_header; cabecera != NULL; cabecera = cabecera->h_link) {
+          /* Si tiene prioridad uno, lo larga YA */
+          if(strcasecmp(cabecera->h_field,"x-priority")==0) {
+            if(atoi(cabecera->h_value)==1) tamanho_por_receptores=0;
+          }
+        }
+
+
        /*
        **  If this message is to be discarded, don't bother sending
        **  the message at all.
@@ -368,6 +466,22 @@
                                q->q_state = QS_QUEUEUP;
                                expensive = true;
                        }
+
+/*
+** jcea@argo.es - 18/May/99
+**
+** Manda los mensajes largos por la noche
+*/
+                        else if (!Verbose && (tamanho_por_receptores>TAMANO_MAXIMO) &&
+                            !bitnset(M_LOCALMAILER,q->q_mailer->m_flags))
+                        {
+                                expensive=true;
+                                q->q_state|=QS_QUEUEUP;
+                        }
+                        else if (!Verbose && envio_diferido) {
+                                expensive=true;
+                                q->q_state|=QS_QUEUEUP;
+                        }
                        else
                        {
                                if (tTd(13, 30))

Este parche incluye los anteriores, por lo que se mantienen las funcionalidades de envío nocturno y prioridad. Pero, adicionalmente, permite la programación de envíos diferidos de forma fiable y precisa.

Básicamente el sistema acepta mensajes entre cuyos destinatarios puede existir uno de la forma "HHMM@envio_diferido". Un mensaje marcado de esa forma se mantendrá en la cola, AL MENOS, durante HH horas y MM minutos. Una vez transcurrido ese tiempo, se procesará de forma normal, estando sujeto a posibles envíos nocturnos, entrega a destinatarios locales, etc.

Pero mientras no haya transcurrido ese tiempo, el mensaje no se entregará a destinos locales, ni se podrá forzar su salida de la cola de forma accidental. En caso de requerir un procesamiento explícito previo al retardo marcado, un administrador de sistemas deberá editar el mensaje directamente en la cola y eliminar el destinatario "marcador", manualmente.

Algunos detalles a tener en cuenta:

  • El retardo especificado SOLO garantiza que el mensaje no será procesado antes de dicho momento. Pero no se garantiza que el mensaje inicie su salida exactamente en ese instante. De hecho ese no será el caso nunca, ya que nuestro sistema de correo, en la actualidad, revisa la cola una vez por hora. Es decir, el mensaje podría permanecer en la cola una hora más. Incluso más tiempo, si hay saturación.

  • Cuando se activa la salida del mensaje, el remitente recibirá un correo de error notificándole que la dirección "HHMM@envio_diferido" no existe. Aunque podríamos haber eliminado ese mensaje de error, nos ha parecido interesante que el remitente reciba algún tipo de indicio de que su mensaje está siendo, finalmente, procesado. No queda muy estético, pero es útil.

  • Las funcionalidades previas de prioridad y envío nocturno siguen funcionando como siempre.

En el momento de escribir este documento, el parche lleva en producción unos seis meses, sin incidencias.



Python Zope ©2005 jcea@jcea.es

Más información sobre los OpenBadges

Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS