Asynchronous and Synchronous Configuration

This article describes the difference between asynchronous and synchronous behavior for Integration Links by analyzing two different use cases.

Asynchronous Configuration (Long-Running Services)

When using an Integration Link with several long-running services, you may run into conflicts between write locks and long-running service calls. Are there any examples of good practice to avoid problems with locking?

When dealing with long-running services, configuring Integration Link service calls synchronously or asynchronously has both benefits and drawbacks. However, to help avoid conflicts between write locks and long-running service calls, we recommend that an asynchronous configuration be used.

Locks and Lost Updates

In order to ensure consistency in a concurrent system, FNZ Studio uses a locking mechanism with exclusive write locks. When a thread holding a lock invokes a long-running service, FNZ Studio force-unlocks the object held by this blocking thread after a certain period of time (120s + maximum 119 s) has elapsed.

After the long-running service completes, the thread may commit changes to the object - but the thread no longer holds a lock on the object. This may lead to lost update situations: meanwhile, other threads may have acquired a lock on the same object, performed changes to that object and then already committed the changes.

As a consequence, service calls should be configured in an asynchronous way, trying to avoid conflicts between write locks and long-running (and thus blocking) service calls.

Option 1. Process Tasks — Configuring Integration Links: Synchronous vs Asynchronous

Whether Integration Links (ILs) run synchronously or asynchronously requires configuration at the Integration Link level (see Figure 1), and at the process task level (see Figure 2).

Figure 1: Integration Link configured to run asynchronously on the Integration Link level, defining a thread pool of 5

Figure 2: Integration Link configured to run asynchronously on the Integration Link Process task level

Based on these two levels of configuration, four combinations are possible.

The table below provides an overview of the different configuration combinations. The quadrants specify the thread usage, and characteristics of the various combinations.

Table 1

Quadrant 1: Synchronous / Synchronous When the IL, and IL Process task, run synchronously, the Process/HTTP thread processes the Integration Link directly. The service call therefore blocks the thread and the user, and may lead to locking problems in cases where the service call takes too long.

Quadrant 2: Synchronous / Asynchronous Here the Process task is configured to run synchronously but the IL is configured to run asynchronously. This means that the Process/HTTP thread is waiting for the IL thread to complete the service call and return, thus blocking the user and reducing thread availability. Also, the Process/HTTP thread may lose their locks if the service call takes too long. As a consequence, lost update situations may occur.

Quadrant 3: Asynchronous / Synchronous The bottom left quadrant illustrates the scenario where the Screen task is processed asynchronously, while the IL is configured to run synchronously. The user is redirected to the portal and the HTTP thread is freed. As the IL cannot take over directly, a camel-internal "ProducerTemplate" thread is used to run the IL and process the service call. The ProducerTemplate thread pool has a size of 10.

Quadrant 4: Asynchronous / Asynchronous The bottom right quadrant summarizes the characteristics of fully asynchronous service call invocation: The IL and the IL Process task are configured to run asynchronously, as illustrated by the settings in Figure 1 and Figure 2 above. This configuration is considered a best practice.

Why Asynchronous / Asynchronous is the Best Option

As soon as the token arrives on the IL Process task, an IL thread takes over and processes the IL components. As a consequence, the Process/ HTTP thread is freed and, in case of a preceding Screen task, the user is redirected to the portal.

The lock on the Process Instance held by that thread is released when the IL thread takes over. The system's availability is improved and no conflicts between long-running operations and the locking mechanism occur. Note that the Process Instance is not locked while the IL is processed.

When the Integration Link is done processing a service call, the IL thread sends a notification to the Process engine: The token sitting on the IL Process task can be moved to the next task. The Process engine thus moves the token immediately after the service call is completed, with no delay or waiting time. This behavior balances out the general load on the Process engine, which, every 5 minutes, checks and then moves tokens if required.

As a best practice, the Integration Link defines assigned variables that are assigned in the Integration Link Process task. Mapping from/ to the service message format is performed outside of the Integration Link (as shown in Figure 2) to ensure that the correct version of the mapping scripts is used. This is because all Process Instances use the same version of the Integration Link: See Integration Link Versioning for more information.

Note: We do not recommend accessing the Process Instance within the IL, as the IL thread does not hold a lock for the Process Instance.

Option 2. Calling Integration Links via Script Functions

When calling Integration Links via the CallIntegrationLink($linkId, $params, $versionFilter, $waitForReply) function, the parameter $waitForReply defines whether or not the current thread waits until the Integration Link is executed.

If $waitForReply=true, the thread executing the Script task waits for the Integration Link call to return before proceeding.

If $waitForReply=false, the Integration Link is processed by a separate thread while the current thread continues processing the script. This is the default behavior.

Table 2 provides an overview of the different configuration combinations. The quadrants specify the thread usage, and characteristics of the various combinations.

Table 2

Quadrant A: CallIntegrationLink ($waitForReply=true) / Synchronous The thread usage and characteristics are the same as for Quadrant 1 in Table 1.

Quadrant B: CallIntegrationLink ($waitForReply=true) / Asynchronous Thread usage and characteristics are the same as Quadrant 2 in Table 1.

Quadrant C: CallIntegrationLink ($waitForReply=false) / Synchronous Thread usage and characteristics are the same as Quadrant 3 in Table 1, with one difference: The Script task / Script action calling the Integration Link is processed further; once completed, the Process continues according to standard FNZ Studio behavior.

Quadrant D: CallIntegrationLink ($waitForReply=false) / Asynchronous Thread usage and characteristics are the same as Quadrant 4 in Table 1, with one difference: The script task / script action calling the Integration Link is processed further; once completed, the Process continues according to standard FNZ Studio behavior.

Note: If CallIntegrationLink(...).get is invoked, the thread waits for the integration task to complete (possibly in a separate thread) to access the returned object, so waitForReply has no effect.

Asynchronous and Synchronous Behavior in the Process Engine

After reading the firs use case above, you have an overview of the possible combinations of Integration Link Task, the CallIntegrationLink Function and their execution modes.

The following sections illustrate how Screen Tasks behave in conjunction with other tasks that make the Process Engine move the token as a result of completion of a background activity (e.g. the completion of an asynchronous Integration Link Task).

A token on a Screen Task gets the Active state only after the scope for the target screen has been initialized. In turn, the scope for the target screen is initialized only when the screen is rendered for the first time, that is, when the user "visits" this workitem for the first time.

The main reason for "deferring" the initialization of the scope until the screen is rendered is that we want to have access to the 'current user' when evaluating the assignment and default expressions for the scope.

Scenario 1

Imagine a situation with two Screen Tasks, in two different Swimlanes, where the completion of Screen Task 1 moves the token to Screen Task 2 in Swimlane 2.

When User 1 in Swimlane 1 clicks Next (or any other action that completes the task), the Process Engine moves the token forward through the process (synchronously in the context of User 1), until it reaches Screen Task 2. Since Screen Task 2 is assigned to another user (User 2), the process engine stops here, and User 1 is redirected to the Inbox.

If the Process Engine immediately initialized the scope for Screen Task 2, functions such as User:CurrentUserId() would still return User 1's ID. To avoid this, the Process Engine only moves the token to Screen Task 2 and then mark it as 'Ready'. This makes the token already visible as a workitem in User 2's Inbox.

When User 2 now opens the workitem, the Screen Task takes care of initializing the screen's scope. If this now triggers the execution of a function such as User:CurrentUserId(), this function returns User 2's ID.

If the two Screen Tasks were in the same Swimlane, the Process Engine would not stop after setting the token to 'Ready'. Instead, the Process Engine would redirect the user to the URL for this token; the token would trigger the rendering of the screen; and the screen would initialize the scope and then set the token to 'Active'.

Scenario 2

Now imagine the following scenario:

2.png

After completing step 1, the AND gateway makes the Process Engine place a token to both screen Tasks:

3.png

The Process Engine then "decides" which screen to show to the user first. When the user looks at this screen, the token on this screen Task becomes 'Active', while the token on the other (not yet rendered) screen is still in state 'Ready':

4.png

In short, a token on a Screen Task only becomes 'Active' when it is "visited" by a user for the first time, where "visited" means that the screen is rendered.

But, how do Integration Links have an impact on this?

  • An async execution of an Integration Link makes the Process Engine stop moving the token when it reaches the Integration Link. The Integration Link is then executed in the background, outside of the context of the HTTP request that started the process (or completed the previous process task). If the Integration Link execution completes, the Process Engine is notified, and it takes care of moving the token forward through the Process Model. However, this is now a background activity. The original HTTP thread/request is probably long gone. Therefore, there is no way to do an HTTP redirect for the user onto the next screen. If the token reaches a Screen Task, it only gets to state 'Ready' because the transition to state 'Active' only happens when the screen is rendered for the first time.
  • A sync execution of an Integration Link behaves differently because all the work is done "in-line" in the same thread by the Process Engine, usually the HTTP request that was used to complete the previous process step (e.g. the click on Next" in the previous screen). If the same user has access to the next Screen Task, the Process Engine redirects the user to this screen directly, the scope gets initialized, and the token state becomes 'Active'.

Conclusion

In general, consider that this problem is not specific to Integration Links, as it is, basically, a timing issue. When is the screen rendered the first time? The screen is rendered when the token becomes 'Active'. If, at some point, the process execution "goes in the background", there is no automatic redirect of the user to the next screen (because background processing could take minutes or even hours). Instead, the user is sent to the Inbox.

Synchronous Configuration (Request-Reply Pattern)

The request-reply scenario for JMS is a standardized pattern to use a synchronous call over asynchronous infrastructure. How to create a JMS request-reply message exchange using ActiveMQ as the message broker?

This section starts with general background information on request-reply message patterns, including a discussion of the more technical aspects. Following is a description on how to implement this pattern using an Integration Link, and provide an example of how to use this Integration Link in a process.

Background

The request-reply message pattern supports quasi-synchronous message exchange. The pattern is realized with a single request queue and temporary reply queues. Request-reply calls are processed by an Integration Link. When calling the Integration Link, an input, i.e. the message, is passed to the Integration Link. The Integration Link returns an output message. In this example, input and output messages are represented by an instance of the class serviceMsg.

Figure 1 illustrates an object, $in, that is passed to the Request-Reply Integration Link. The Request-Reply Integration Link returns a serviceMsg, $out.

Figure 1: Integration Link Input/Output

How message processing works:

  • The Integration Link sends a serialized version of the serviceMsg instance within the body of the Camel Exchange Message to a specified message queue
  • Behind the scenes, Camel/Active MQ generates a temporary queue for the reply, and registers a listener for that queue
  • The reply message is sent back within the Camel Exchange message body
  • Once the reply is received, the Integration Link proceeds with its execution of de-serializing the XML into a serviceMsg instance

These operations are all handled inside the Integration Link, and do not have to be configured or changed when doing message exchange.

Technical Characteristics

A request/reply service call makes use of a number of threads: one thread for the process user/task (HTTP) and one thread for the Integration Link. As the Integration Link is configured to work asynchronously with a pool of x workers, additional x-1 more threads will be used. The configuration should be tested under load with five workers. The number of workers should be increased if needed (inspection using Camel JMX).

The performance of this approach is better than making use of a dedicated reply queue. With the approach outlined here, each call gets its own (temporary) reply queue and, as a consequence, does not have to wait in a general reply queue.

In the case of long-running or slow service calls, the service call may stall. To prevent the operation from blocking the user, the task should be configured as "asynchronous".

Integration Link Configuration

The integration link configuration displayed below makes use of the request-reply message pattern. Select this pattern from the Message Pattern dropdown list (shown at the bottom left of the image).

Figure 2: Request/Reply Integration Link Configuration

The request/reply Integration Link consists of five Integration Link components. Each Integration Link component takes an $in variable from the preceding component, and each component’s return value is passed to the component that follows it.

  1. The Integration Link "start" element is configured to work asynchronously with a pool of five workers, and a timeout of 30000ms. In case of heavy load, the worker pool can be increased.
  2. The scripted processor component converts the incoming object $in into a serialized representation, such as XML, as shown below.
    Copy
    Named String $prefixes := new Named String;
    $prefixes['http://appway.com/nsexample'] := 'APP';
    SOAPConvertToXML($in, $prefixes, false, false, false, 'http://appway.com/nsexample');
  3. The ActiveMQ notifier is configured to listen to a specific queue or topic. The topic can either be fixed or dynamic (that is, based on a variable).
  4. The ActiveMQ notifier produces output consisting of the Camel Exchange message body. This output is transformed to a string by the BodyToString component.
  5. The scripted processor component transforms the XML string received by the previous component into the appropriate data entity, as shown in the code snippet below.
    Copy
    XmlNode $node := ParseXmlDocument($in, true);
    Named String $prefixes := new Named String;
    $prefixes['http://appway.com/nsexample'] := 'APP';
    SOAPConvertToDataEntity($node, $prefixes, false);
Note:

The Integration Link only commits (to Hazelcast) once the complete chain of Integration Link components has been processed. For operations within the Integration Link that require a commit prior to Integration Link completion, a manual commit operation has to be invoked.

Parameterization Data exchange is based on predefined well-structured XML messages. There are two options available as to how to apply the presented pattern: A dedicated Integration Link could be used for each message exchange type within an FNZ Studio solution, or a generic Integration Link could be used for all types of message exchanges defined for a specific solution. With a generic Integration Link, there is a single point responsible for message exchange-related integration, allowing reuse while minimizing maintenance efforts.

Execution The Integration Link can either be called programmatically, or embedded into a process. To call the Integration Link programmatically, use this script:

Copy
Named Any $params := new Named Any;
$params['serviceMsg'] := $in;
CallIntegrationLink('RRIntegrationLink', $params, $versionFilter, true);

To embed the Integration Link into a process, follow the steps outlined below in "Integration Link Task Configuration".

Integration Link Task Configuration

An Integration Link can be embedded into a process using an Integration Link Process Task configured according to Figure 3. The Integration Link defines an assigned variable of type, serviceMsg. This assigned variable has to be defined on a Process level, then assigned during Integration Link process task configuration.

The configuration defines:

  • Which Integration Link is invoked
  • The version filter that should be employed
  • The variable to which the result should be bound

Figure 3: Process with embedded Integration Link

Note:

In my example, the data mapping script task precedes and follows the Integration Link task, where the mapping from process domain data classes to serviceMsg instance and back is performed. Depending on process complexity, it may make sense to create a subprocess consisting of the mapping script tasks and the Integration Link task to decrease visual process complexity.<br/> In order to execute an integration link asynchronously, the asynchronous checkbox in the Integration Link task process element can be checked. The user is then redirected to the portal, and a new work item can be generated on the fly. Alternatively, a "wait" screen task with auto-submit may precede the tasks to inform the user about a long-running operation.