Artículo
· 3 hr atrás Lectura de 5 min

¿Cómo ejecutar un proceso en intervalos o según una programación?

Cuando comencé mi trayectoria con InterSystems IRIS, especialmente en el área de Interoperabilidad, una de las preguntas iniciales y más comunes que tuve fue: ¿cómo puedo ejecutar algo en intervalos o según una programación? En este artículo quiero compartir dos clases sencillas que abordan este problema. Me sorprende que no haya clases similares ubicadas en algún lugar de EnsLib. ¿O quizá no busqué bien? En cualquier caso, este artículo no pretende ser un excesivamente complejo, sino una muestra de un par de fragmentos para principiantes.

Así que supongamos que tenemos una tarea: “Tomar algunos datos de una API y colocarlos en una base de datos externa”. Para resolver esta tarea, necesitamos:

  1. Ens.BusinessProcess, que contiene el algoritmo de nuestro flujo de datos: cómo preparar una solicitud para obtener los datos, cómo transformar la respuesta de la API en una solicitud para la base de datos, cómo manejar los errores y otros eventos a lo largo del ciclo de vida del flujo de datos. 
  2. EnsLib.REST.Operation para realizar solicitudes HTTP a la API utilizando EnsLib.HTTP.OutboundAdapter
  3. Ens.BusinessOperation con EnsLib.SQL.OutboundAdapter para insertar los datos en la base de datos externa a través de una conexión JDBC.

Los detalles de la implementación de estos hosts de negocio quedan fuera del alcance de este artículo, así que supongamos que ya tenemos un proceso y dos operaciones. Pero, ¿cómo ejecutarlo todo? El proceso solo puede ejecutarse mediante una solicitud entrante… ¡Necesitamos un iniciador! Uno que se ejecute a intervalos y envíe una solicitud ficticia a nuestro proceso.

Aquí tenemos una clase de iniciador de ese tipo. Le añadí un poco de funcionalidad adicional: se podrán usar llamadas síncronas o asíncronas, y decidir si detener o no el proceso en caso de error si tenemos varios hosts como destino. Pero lo principal aquí es la lista de destinos. A cada elemento (host de negocio) de esta lista se le enviará una solicitud. Prestad atención al evento OnGetConnections: es necesario para construir correctamente los enlaces en la interfaz de producción.

/// Call targets by interval
Class Util.Service.IntervalCall Extends Ens.BusinessService
{

/// List of targets to call
Property TargetConfigNames As Ens.DataType.ConfigName;
/// If true, calls are made asynchronously (SendRequestAsync)
Property AsyncCall As %Boolean;
/// If true, and the target list contains more than one target, the process will stop after the first error
Property BreakOnError As %Boolean [ InitialExpression = 1 ];
Property Adapter As Ens.InboundAdapter;
Parameter ADAPTER = "Ens.InboundAdapter";
Parameter SETTINGS = "TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},AsyncCall,BreakOnError";
Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject, ByRef pHint As %String) As %Status
{
    Set tSC = $$$OK
    Set targets = $LISTFROMSTRING(..TargetConfigNames)

    Quit:$LISTLENGTH(targets)=0 $$$ERROR($$$GeneralError, "TargetConfigNames are not defined")

    For i=1:1:$LISTLENGTH(targets) {
        Set target = $LISTGET(targets, i)
        Set pRequest = ##class(Ens.Request).%New()

        If ..AsyncCall {
            Set tSC = ..SendRequestAsync(target, pRequest)
        } Else  {
            Set tSC = ..SendRequestSync(target, pRequest, .pResponse)
        }
        Quit:($$$ISERR(tSC)&&..BreakOnError)
    }

    Quit tSC
}

ClassMethod OnGetConnections(Output pArray As %String, pItem As Ens.Config.Item)
{
    If pItem.GetModifiedSetting("TargetConfigNames", .tValue) {
        Set targets = $LISTFROMSTRING(tValue)
        For i=1:1:$LISTLENGTH(targets) Set pArray($LISTGET(targets, i)) = ""
    }
}

}

Después de eso, solo necesitáis añadir esta clase a la Producción y marcar nuestro proceso de negocio en la configuración TargetConfigNames.

Pero, ¿qué pasa si los requisitos cambian? Y ahora necesitamos ejecutar nuestro recolector de datos todos los lunes a las 08:00 a. m. La mejor manera de hacerlo es utilizando el Administrador de Tareas. Para ello, debemos crear una tarea personalizada que ejecute nuestro Iniciador de forma programada. Aquí tenéis un código sencillo para esa tarea:

/// Launch selected business service on schedule
Class Util.Task.ScheduleCall Extends %SYS.Task.Definition
{

Parameter TaskName = "Launch On Schedule";
/// Business Service to launch
Property ServiceName As Ens.DataType.ConfigName;
Method OnTask() As %Status
{
    #dim tService As Ens.BusinessService
    Set tSC = ##class(Ens.Director).CreateBusinessService(..ServiceName, .tService)
    Quit:$$$ISERR(tSC) tSC
    
    Set pRequest = ##class(Ens.Request).%New()
    Quit tService.ProcessInput(pRequest, .pResponse)
}

}

Dos cosas importantes aquí:

  • Debéis establecer el tamaño del grupo (Pool Size) del Servicio de Negocio Iniciador en 0 para evitar que se ejecute por Call Interval (la opción Call Interval se puede borrar o dejar tal cual, ya que no se usa cuando el Pool Size es 0).

             

  • Necesitáis crear una tarea en el Task Manager, elegir “Launch On Schedule” como tipo de tarea (no olvidéis comprobar el Namespace), establecer el nombre de nuestro ServiceName Iniciador en el parámetro ServiceName y configurar la programación deseada. Consultad: Operaciones del sistema > Administrador de tareas > Nueva tarea.

Y un bonus

A menudo me he encontrado con casos en los que necesitamos ejecutar algo en Producción solo bajo demanda. Por supuesto, podríamos crear una interfaz personalizada en CSP para ello, pero reinventar la rueda no es nuestro camino. Creo que es mejor utilizar la interfaz típica del Portal de Administración. Así que la misma tarea que creamos anteriormente se puede ejecutar manualmente. Solo hay que cambiar el tipo de ejecución de la tarea a “On Demand”. La lista de tareas On Demandestá disponible en System > Task Manager > On-demand Tasks, donde veréis el botón Run. Además, el botón Run(ejecución manual) está disponible para cualquier tipo de tarea.

Eso es todo. Ahora tenemos una arquitectura de interoperabilidad bastante sólida para nuestros hosts de negocio, y tres formas de ejecutar nuestro recolector de datos: por intervalo, según un horario o manualmente.

Comentarios (0)0
Inicie sesión o regístrese para continuar