Written by

Senior Cloud Architect at InterSystems
Question Eduard Lebedyuk · Jan 11, 2023

Inbound Adapter safe shutdown strategy

In Interoperability productions Inbound Adapters extract and separate retrieval logic from actual payload processing, which is left to a BS.

At a high level, an adapter looks like this:

Class MyAdapter Extends Ens.InboundAdapter {

Method OnTask() As%Status
{
  Set request = ..RetrieveRequest()
  Set sc = ..BusinessHost.ProcessInput(request)
  Set..BusinessHost.%WaitForNextCallInterval=1
  Quit sc

}
}

However, in many cases, RetrieveRequest retrieves a batch payload, so our adapter looks like this instead:

Class MyAdapter Extends Ens.InboundAdapter {


Method OnTask() As%Status
{
  Set requests = ..RetrieveRequests()
  For i=1:1:requests.Count() {
    Set sc = ..BusinessHost.ProcessInput(requests.GetAt(i))
  }
  Set..BusinessHost.%WaitForNextCallInterval=1Quit sc
}

}

Batch processing is popular due to a variety of reasons:

  • It's usually faster
  • On the external system side, it allows easier scaling
  • That's the only way the external system can work

There is, however, a problem if a batch is too large or individual processing time is too long - the entire time OnTask runs, Adapter and the corresponding Business Service can't be shut down. The EnableConfigItem/TempStopConfigItem calls would fail unless forced, which is also harmful as it can affect forced BS and other BHs scheduled to stop.

My question: is there a cheap call I can make from inside the loop to check if there's an external BH disable request?

Of course, already retrieved but unprocessed requests must be compensated (a user needs to roll unprocessed batch items back if that's applicable at all), but assuming that is doable, how can I check for an external disable event?

My idea is something like this:

Class MyAdapter Extends Ens.InboundAdapter {


Method OnTask() As%Status
{
  Set requests = ..RetrieveRequests()
  For i=1:1:requests.Count() {
    If..GotExternalDisableEvent() {
      Set..BusinessHost.%WaitForNextCallInterval=1Return..CompensateUnprocessedRequests(requests, i)
    }
    Set sc = ..BusinessHost.ProcessInput(requests.GetAt(i))
  }
  Set..BusinessHost.%WaitForNextCallInterval=1Quit sc
}

}

Calling @James MacKeith and @Dmitry Zasypkin and @sween.

Product version: IRIS 2022.1
$ZV: IRIS for Windows (x86-64) 2022.1 (Build 209U) Tue May 31 2022 12:16:40 EDT

Comments

Enrico Parisi · Jan 11, 2023

Exactly for that situation I use:

If##class(Ens.Job).ShouldBeQuiescent() || ##class(Ens.Job).ShouldTerminate() {
    ; do your closing housekeeping or...whatever..Quit
}

In your case maybe only ShouldTerminate() would be sufficient.

Enrico

0
Eduard Lebedyuk  Jan 12, 2023 to Enrico Parisi

Thank you! That is exectly what I need.

0
James MacKeith  Jan 12, 2023 to Eduard Lebedyuk

Perhaps also you can limit calling the Shouldxxx()  based on update production timeout (for example) rather than checking every request in the collection.

James

0
Eduard Lebedyuk  Jan 12, 2023 to James MacKeith

Checking every 5 seconds should be good enough I think (with 10 being a default timeout in several places).

This is actually for a PEX adapter but it looks like a fast check.

0