En el artículo anterior, hablábamos acerca del Kernel Terminate y de cómo podemos utilizarlo, para realizar acciones pesadas una vez terminado el procesamiento de nuestro sitio web.

Al final del artículo, os explicaba un pequeño inconveniente al usar este estado y de cual sería una posible solución, por eso hoy os traigo este articulo, en cual vamos a verlo un poco más en profundidad.

El problema

El mayor inconveniente que nos encontramos a la hora de desarrollar procesos que son más pesados o duraderos de lo habitual es, que tienden a colapsar la web y/o ralentizarla  haciendo en muchas ocasiones que nosotros mismos nos creemos ataques DDOS sin quererlo.

Una de las soluciones más sencillas que nos provee drupal es sin duda el cron, el cual, aparte de usar el servicio «lock», nos permite ejecutar código de tal modo que no bloquee ni colapse nuestro servidor.

el servicio LOCK para solucionarlo

Por esta razón se creó el servicio «LOCK» el cual nos permite bloquear una acción y no ejecutarla de nuevo hasta que no esté terminada.

Veámoslo en un ejemplo para verlo más claro, imaginemos que tenemos un sistema que sincroniza nuestras ventas con un CRM y en lugar de usar cron usamos el KERNEL TERMINATE, para que al final de cada carga de la web se realice esta sincronización y así tener la información prácticamente al instante.

Como norma, las integraciones a agentes externos suele llevar un tiempo, dependiendo de cómo lo hayan programado, esto hace que, si nuestro sitio recibe muchas visitas tengamos que limitar de algún modo esta sincronización por el bien del servidor.

Veamos algo de codigo:

 

				
					<?php

namespace Drupal\dummy_terminate\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event;
use Drupal\Core\Lock\LockBackendInterface;

/**
 * Class MyEventSubscriber.
 */
class MyEventSubscriber implements EventSubscriberInterface {

  /**
   * Drupal\Core\Lock\LockBackendInterface definition
   * 
   * @var \Drupal\Core\Lock\LockBackendInterface
   */
  protected $lock;

  /**
   * Constructs a new MyEventSubscriber object.
   */
  public function __construct(LockBackendInterface $lock) {
    $this->lock = $lock;
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events['kernel.terminate'] = ['mi_metodo_super_pesado'];

    return $events;
  }

  /**
   * This method is called when the kernel.terminate is dispatched.
   *
   * @param \Symfony\Component\EventDispatcher\Event $event
   *   The dispatched event.
   */
  public function mi_metodo_super_pesado(Event $event) {
    @ignore_user_abort(TRUE);
    // Primero consultamos si nuetra accion pesada esta en ejecucion.
    if ($this->lock->acquire('sync_crm , 900.0)) {
      // Mi logica de sincronizacion con el CRM
      mi_super_funcion_de_sync();
      
      // Una vez terminada la logica liberamos la accion para que pueda ser llamada de nuevo.
      $lock->release('sync_crm');
    }
  }

}
				
			

Si revisamos el código anterior podremos observar varias cosas interesantes.

Por un lado, tenemos la forma como consultamos si la acción está en ejecución o no. Con el «$this->lock->acquire()» lo que hacemos es preguntar si la acción ha sido lanzada y/o esta activa, en caso de que no exista la crea.

Requiere de dos parámetros, uno es el nombre de la acción para poder trackear y el timeout que es básicamente el límite de tiempo en milisegundos que tiene para ejecutar la acción, nos permite controlar cada cuanto tiempo podemos ejecutar la acción (valor 30ms por defecto).

Por otro lado tenemos el «$lock->release()«, que lo que hace es básicamente liberar la tarea para que pueda volver a ser ejecutada. Esto hará que cuando vuelva al acquire, este pueda volver a ejecutar la acción.

Como veis es un concepto bastante sencillo de bloqueo y liberación constante de acciones, pero aun simple, con esto podremos garantizar que nuestra aplicación no se sobrecargue y que las acciones solo se ejecuten a medida que se vayan terminando.

ATENCION lock vs lock.persistent

Es importante que tengáis en cuenta esto cuando uséis el servicio «lock» y es que existen dos tipos, y aunque en esencia hacen y funcionan de la misma forma, conceptualmente son diferentes y se usan para diferentes escenarios.

  • lock: Solo bloquea la acción en tiempo de ejecución una ver terminada toda la ejecución o carga de la web realiza el «release» automáticamente.
  • lock.persistent: El «release» se tiene que hacer de forma manual si no, quedará almacenada en la base de datos para siempre.

Conclusión

Como veis ahora podréis ejecutar tareas pesadas de manera repetitiva sin miedo a que el servidor colapse, ya que podemos controlar cuando y como se ejecutan estas acciones, sin miedo a que se sobrepongan o se sobrecarguen.

Usando correctamente el servicio lock podremos realizar acciones bastante interesantes sin miedo a perder nuestro servidor y si queréis saber más siempre podéis mirar como funciona el cron de drupal, el cual usa este servicio para cada vez que ejecutamos el cron de drupal.

Espero que os sirva este pequeño artículo y si os ayuda comentar como os ayudo el servicio lock