4.5 Plugins Síncronos vs Asíncronos

Elige el modo correcto según tu escenario

Cuando registras un plugin, debes decidir si se ejecuta de forma síncrona o asíncrona. Esta decisión tiene implicaciones profundas en la experiencia del usuario, en el comportamiento de transacciones, y en cómo manejas errores. No es una elección trivial.

Objetivos de aprendizaje

  • Entender las diferencias fundamentales entre ejecución síncrona y asíncrona
  • Elegir el modo correcto según el escenario
  • Monitorear y gestionar jobs asíncronos
  • Implementar patrones apropiados para cada modo

Síncrono: inmediato pero bloqueante

Un plugin síncrono se ejecuta como parte de la operación del usuario. El usuario hace clic en "Guardar", el sistema procesa la operación, ejecuta tus plugins, y solo cuando todo termina el usuario ve el resultado.

Esta inmediatez tiene ventajas:

  • El usuario sabe inmediatamente si algo falló
  • Puedes cancelar la operación y mostrar mensajes de error
  • Participas en la transacción, el rollback es automático
  • El orden de ejecución está garantizado

Pero también tiene desventajas:

  • El usuario tiene que esperar a que tu código termine
  • Hay un timeout de 2 minutos, si lo excedes la operación falla
  • Un plugin lento afecta la experiencia de todos los usuarios

Stages disponibles

Los plugins síncronos pueden registrarse en cualquier stage:

  • Pre-validation (10): antes de la transacción
  • Pre-operation (20): dentro de la transacción, antes de la operación
  • Post-operation (40): dentro de la transacción, después de la operación

Asíncrono: no bloqueante pero desconectado

Un plugin asíncrono se registra para ejecutarse "más tarde". El usuario guarda el registro, la operación se completa, y el sistema crea un "System Job" que eventualmente ejecutará tu plugin.

Esto cambia completamente la dinámica:

  • El usuario no espera, la respuesta es inmediata
  • Tienes hasta una hora de timeout (mucho más que los 2 minutos síncronos)
  • Si fallas, el sistema reintenta automáticamente (hasta 3 veces)
  • NO puedes cancelar la operación original, ya se completó
  • NO participas en la transacción

Solo Post-operation

Los plugins asíncronos solo pueden registrarse en Post-operation. Tiene sentido: la operación ya debe haberse completado para poder ejecutar en segundo plano.

El retraso de ejecución

Cuando registro un plugin asíncrono, el código no se ejecuta inmediatamente. Puede pasar desde unos segundos hasta varios minutos, dependiendo de la carga del sistema. No asumas ejecución instantánea.


Cuándo usar cada modo

Esta guía te ayuda a elegir:

Usa síncrono cuando:

  • Necesitas validar y potencialmente rechazar la operación
  • La operación debe completarse junto con la principal (transacción)
  • El orden de ejecución es crítico
  • El usuario debe ver el resultado inmediatamente
  • Ejemplos: validaciones, cálculos de campos, auto-numeración, asignación automática

Usa asíncrono cuando:

  • La operación no es crítica para el éxito de la principal
  • Puede tomar tiempo (llamadas a servicios externos, procesamiento pesado)
  • Puede fallar sin afectar al usuario
  • No necesitas modificar datos de la operación original
  • Ejemplos: notificaciones, sincronizaciones, auditoría extendida, análisis

Ejemplo: sincronización con ERP

Ana tiene un plugin que sincroniza cuentas con el ERP de la empresa. La llamada al ERP puede tardar 5-10 segundos.

Si lo hace síncrono:

  • Cada vez que alguien guarda una cuenta, espera 5-10 segundos
  • Si el ERP está caído, nadie puede guardar cuentas
  • Los usuarios se frustran con la lentitud

Si lo hace asíncrono:

  • El usuario guarda y continúa trabajando inmediatamente
  • La sincronización ocurre en background
  • Si el ERP falla, el plugin se reintenta automáticamente
  • Si sigue fallando, queda un registro en System Jobs para investigar

La sincronización asíncrona es claramente mejor aquí.

Ejemplo: validación de límite de crédito

Carlos tiene un plugin que verifica si el cliente tiene crédito disponible antes de crear un pedido.

Si lo hace asíncrono:

  • El pedido se crea aunque el cliente no tenga crédito
  • Luego el plugin asíncrono detecta el problema... ¿y qué hace?
  • Ya hay un pedido inválido en el sistema

Si lo hace síncrono:

  • Verifica el crédito antes de completar la operación
  • Si no hay crédito, rechaza el pedido con mensaje claro
  • El usuario sabe inmediatamente qué pasó

La validación debe ser síncrona.


Monitoreando jobs asíncronos

Los plugins asíncronos crean registros en la entidad System Job (asyncoperation). Puedes monitorear el estado de tus plugins consultando esta entidad:


// Buscar jobs fallidos de un plugin específico
QueryExpression query = new QueryExpression("asyncoperation");
query.ColumnSet = new ColumnSet("name", "friendlymessage", "createdon", "statuscode");

// statuscode 31 = Failed
query.Criteria.AddCondition("statuscode", ConditionOperator.Equal, 31);

// Filtrar por nombre del plugin
query.Criteria.AddCondition("name", ConditionOperator.Contains, "MiPluginAsync");

query.AddOrder("createdon", OrderType.Descending);
query.TopCount = 20;

EntityCollection jobsFallidos = service.RetrieveMultiple(query);

foreach (Entity job in jobsFallidos.Entities)
{
    string nombre = job.GetAttributeValue("name");
    string error = job.GetAttributeValue("friendlymessage");
    DateTime fecha = job.GetAttributeValue("createdon");
    
    trace.Trace($"[{fecha}] {nombre}: {error}");
}

En la aplicación, los administradores pueden ver System Jobs en Settings → System Jobs. Ahí pueden ver qué plugins fallaron, cuántas veces se reintentaron, y el mensaje de error.


Reintentos automáticos

Cuando un plugin asíncrono lanza una excepción, el sistema lo reintenta automáticamente:

  • Primer reintento: aproximadamente 10 minutos después
  • Segundo reintento: aproximadamente 20 minutos después del primero
  • Tercer reintento: aproximadamente 30 minutos después del segundo
  • Después del tercer fallo: el job queda marcado como Failed

Esta política de reintentos es útil para errores transitorios (el servicio externo estaba momentáneamente no disponible), pero puede ser problemática para errores permanentes (datos incorrectos que nunca funcionarán).


// Diferenciar errores transitorios de permanentes
public void Execute(IServiceProvider serviceProvider)
{
    var context = (IPluginExecutionContext)
        serviceProvider.GetService(typeof(IPluginExecutionContext));
    
    try
    {
        SincronizarConServicioExterno();
    }
    catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable)
    {
        // Error transitorio: permitir reintento
        throw new InvalidPluginExecutionException(
            "Servicio externo no disponible, reintentando...", ex);
    }
    catch (ValidationException ex)
    {
        // Error permanente: los datos son incorrectos
        // NO relanzar excepción para evitar reintentos inútiles
        MarcarRegistroConError(context.PrimaryEntityId, ex.Message);
        return;  // Terminar sin excepción
    }
}

Puntos clave

  • Síncrono: inmediato, 2 min timeout, participa en transacción, puede rechazar operaciones
  • Asíncrono: diferido, ~1 hora timeout, fuera de transacción, reintentos automáticos
  • Usa síncrono para validaciones y operaciones que deben ser atómicas
  • Usa asíncrono para tareas secundarias, notificaciones, sincronizaciones
  • Monitorea System Jobs para ver estado de plugins asíncronos
  • Diferencia errores transitorios (dejar reintentar) de permanentes (evitar reintentos)

Para profundizar

Inicia sesión e inscríbete para guardar tu progreso.
En este curso
¿Te ha resultado útil?