Article
· 13 hr ago 4m read

How to run a process on an interval or schedule?

When I started my journey with InterSystems IRIS, especially in Interoperability, one of the initial and common questions I had was: how can I run something on an interval or schedule? In this topic, I want to share two simple classes that address this issue. I'm surprised that some similar classes are not located somewhere in EnsLib. Or maybe I didn't search well? Anyway, this topic is not meant to be complex work, just a couple of snippets for beginners.

So let's assume we have a task "Take some data from an API and put it into an external database". To solve this task, we need:

  1. Ens.BusinessProcess, which contains an algorithm of our data flow: How to prepare a request for taking data, how to transform the API response to a request for DB, how to handle errors and other events through the data flow lifecycle
  2. EnsLib.REST.Operation for making HTTP requests to the API using EnsLib.HTTP.OutboundAdapter
  3. Ens.BusinessOperation with EnsLib.SQL.OutboundAdapter for putting data into the external database via a JDBC connection

Details of the implementation of these business hosts lie outside the scope of this article, so let's say we already have a process and two operations. But how to run it all? The process can run only by inbound request... We need an Initiator! Which one will just be run by interval and send a dummy request to our process.

Here is such an initiator class. I added a bit of additional functionality: sync or async calls will be used, and stop or not process on error if we have many hosts as targets. But mainly here it's a target list. To each item (business host) on this list will be sent a request. Pay attention to the OnGetConnections event - it's needed for correct link building in Production UI.

/// 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)) = ""
    }
}

}

After it, you just need to add this class to Production, and mark our business process in the TargetConfigNames setting. 

But what if requirements were changed? And now we need to run our data grabber every Monday at 08:00 AM. The best way for it is using Task Manager. For this, we need to create a custom task that will run our Initiator programmatically. Here is a simple code for this task:

/// 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)
}

}

Two important things here:

  • You must set the Pool Size of the Initiator Business Service to 0 to prevent running it by call interval (option Call Interval, you can clear or leave as is - it's not used when Pool Size is 0)

             

  • You need to create a task in Task Manager, choose "Launch On Schedule" as task type (don't forget to check a Namespace), set our Initiator Business Service name to the ServiceName parameter, and set up the desired schedule. See: System Operation > Task Manager > New Task

And a bonus

I often faced cases when we need to run something in Production only on demand. Of course, we can create some custom UI on CSP for it, but reinventing the wheel is not our way. I believe it is better to use the typical UI of the Management Portal. So, the same task that we created previously can be run manually. Just change the task run type to On Demand for it. On-demand task list is available at System > Task Manager > On-demand Tasks, see the Run button. Furthermore, the Run button (manual run) is available for any kind of task.

It is all. Now we have a pretty architecture of interoperability for our business hosts. And 3 ways to run our data grabber: by interval, on a timetable, or manually.

Discussion (0)1
Log in or sign up to continue