Async Service Events - learn-tibco-cep/tutorials GitHub Wiki

The Async Service handler subscribes 2 event types (client-request and service-response) and publishes 2 event types (service-request and client-response). This page describes the design of these events and practical tips when they are carried on FTL channel destinations.

ClientRequest

This event is published by a client application. When the Async Service receives an event of this type, it starts the lifecycle of a new service handler.

The event contains the following properties.

Name Type Description
message String Content of the request
requestTime long Timestamp when the event is created by a client

For more complex request, it may be better to use an event payload to carry an XML or JSON document (see Tips section below for more details). This tutorial uses only a simple message property.

Default destination

This event defines its default destination as clientRequest on FTLClient channel. It is not absolutely required for the service itself, but it helps event publishers such as a Test Driver to route the event to its most common destination.

Preprocessor function

The function onClientRequest is assigned as the preprocessor function of the destination clientRequest as shown in the Demo.cdd.

This function is executed for each event received on this destination. It will create a new handler to track the progress of the request lifecycle, and send out a Service Request to start the asynchronous process.

ClientResponse

This event is published by the Async Service handler after it has collected all expected service responses, or when the process has timed out.

The event contains the following properties.

Name Type Description
message String Summary message of the response
requestTime long Timestamp when the corresponding request was created
receiptTime long Timestamp when the corresponding request was received
responseTime long Timestamp when the response is created
status String Complete or Timeout

This event will include a payload that is a JSON document containing multiple service responses (see Tips section below for more details about event payload).

The function sendClientResponse is implemented to create the JSON payload and to publish the event to its default destination. Note that the JSON payload is constructed by simple string manipulation in this tutorial, while more complex JSON document should consider more bulletproof ways for JSON object serialization.

Default destination

This event defines its default destination as clientResponse on FTLClient channel. Thus, events of this type will be routed to this destination unless specified otherwise.

Preprocessor function

The consumer of this event type, e.g., Test Driver, assigns a preprocessor function onClientResponse to the destination clientResponse as shown in the Demo.cdd.

This function is executed for each event received on this destination. The Test Driver uses this function to aggregate performance statistics of the end-to-end process.

ServiceRequest

This event is published by the Async Service handler to start a new process after it receives a client request. The event is supposed to be processed by one or more service providers that can gather relevant business data and return them back to the Async Service handler.

The event contains the following properties.

Name Type Description
correlationId String The unique ID of the service handler that service provider should send back with service responses
message String Content of the request
requestTime long Timestamp when the request is created
expectedResponses long Number of expected service responses

This event relays a client request to service providers who have access to actual business data. It is constructed and published by the function onClientRequest, which generates a random number expectedResponses to simulate the business logic for requesting multiple responses from target service providers.

Default destination

This event defines its default destination as serviceRequest on FTLService channel. Thus, events of this type will be routed to this destination unless specified otherwise.

Preprocessor function

The consumer of this event type, e.g., Mock Service, assigns a preprocessor function onServiceRequest to the destination serviceRequest as shown in the Demo.cdd.

This function is executed for each event received on this destination. The Mock Service is implemented to publish the expected number of responses immediately after it receives a service request.

ServiceResponse

This event is published by a service provider that has access to business data. When the Async Service receives an event of this type, it will find the corresponding service handler for the matching correlation ID of the event, and then the handler can collect the service response data and return them to the requestor when the process completes.

The event contains the following properties.

Name Type Description
correlationId String Unique ID of the service handler for corresponding client request
response String Content of the service response
receiptTime long Timestamp when the service provider received the corresponding service request
responseTime long Timestamp when the response is created by the service provider

For more complex service responses, it may be better to use an event payload to carry an XML or JSON document (see Tips section below for more details). This tutorial uses only a simple response property.

Default destination

This event defines its default destination as serviceResponse on FTLService channel. It is not absolutely required for the service itself, but it helps event publishers such as a Mock Service to route the event to its most common destination.

Preprocessor function

The function onServiceResponse is assigned as the preprocessor function of the destination serviceResponse as shown in the Demo.cdd.

This function is executed for each event received on this destination. It will retrieve the corresponding service handler, acquire necessary locks, and then let the handler's state-machine to process the event.

Tips

Following are some tips that may be helpful when you design events and preprocessor functions. Some of the tips are specifically for FTL destinations.

Acquire locks in preprocessor functions

Locks are required for a multi-threaded process to serialize the updates of a stateful data object. For example, the ServiceResponse event will lead to update to the state of a Handler object that matches the event correlationId. The preprocessor function for this event will first acquire a lock on the correlationId, and so multiple response events for the same correlationId will be processed sequentially, and multiple updates to the handler state will not override each other.

To avoid lock contention and dead lock situations, following good practices should be considered.

  • Acquire fine-grained locks so long as it is good enough for serialized execution, e.g., lock on a single handler object in this tutorial.
  • Acquire locks in event preprocessor function, instead of rule actions. In a rule-based system such as BE, you have less control on the sequence of rule execution, and thus, locks acquired in rule actions may lead to unintended results. You should avoid acquiring locks directly or indirectly in a rule action even when Concurrent RTC is used to reduce the overall performance hit of locks.
  • Do not acquire multiple locks in different order. If an event preprocessor must acquire more than one locks, it increases the risk of dead-lock situation. The order of locks in all such preprocessor functions must be evaluated together to avoid dead-lock scenarios.

Update state of concept instances in rules only

It is normal to create a new concept instance and reset its properties in an event preprocessor function. However, if a preprocessor function retrieves an existing concept instance from engine's working memory or a cluster cache or persistent store, all updates to the concept instance should be performed in a rule action. Otherwise, if you update the concept directly in a preprocessor function, concurrent execution of multiple preprocessors may override each other, which may result in data loss.

Do not use int type for event properties

You may have noticed that the property expectedResponses in the event ServiceRequest should not be a large number, and thus it could be defined to use int type, instead of the long type.

However, at the time of this writing, the event serializer for FTL destinations does not support int type. Thus, you should always use long type for integer event properties.

BE generated fields in FTL message format

FTL messages published by BE applications contain the following additional fields.

Field Type Example
_ns_ string www.tibco.com/be/ontology/Events/ClientRequest
_nm_ string ClientRequest
_payload_ string Serialized text of event payload

Although these fields are not required for BE applications to consume FTL messages, they are useful for BE consumer application to deserialize FTL messages into desired BE event types, especially when a FTL message is received on a FTL destination that does not specify a default event type.

The value of _nm_ or _ns_ in a FTL message may be used to define the Content Matcher of a FTL message consumer, and so only the specified event type will be sent to the consumer. For example, the FTLService channel defines a Content Matcher {"_nm_": "ServiceRequest"} for the destination serviceRequest.

The value of _payload_ in a FTL message can be read by a BE application the same way as any event payload, i.e., myEvent@payload.

Send event using outbound channel destinations

One way outbound messages are typically sent to the default destination of an event by calling the catalog function sendEvent, or sent to a specified destination by calling routeTo. The timing of the event sent, however, depends on where these functions are invoked.

If sendEvent or routeTo are called from an event preprocessor function, or a timer event rule, the messages will be sent outbound immediately. This is often a required behavior such as in the Test Driver and Mock Service of this tutorial where we want to send messages immediately when these functions are called.

When they are called in a rule, no matter it is a standalone rule or a part of a state machine, the messages will not be sent until post RTC, i.e., after all rules are executed successfully. Such behavior as an atomic group of work is also useful in cases similar to the following example.

In this tutorial, when the Async Service receives a client request, it wants to send out a service request to the Mock Service to fetch response data. We could send the ServiceRequestEvent directly from the preprocessor function of the ClientRequestEvent, i.e., the function onClientRequest. However, such implementation would send out the service request immediately before the lifecycle Handler is stored in cache. Thus, when a cache or persistent store is used to share the lifecycle handler across multiple inference engines, such implementation would lead to a race condition when service responses may be received before the Handler is available to process them. To avoid such race conditions, the preprocessor function only sets a condition to trigger a rule sendServiceRequest, which will send out a service request in post RTC after the new lifecycle handler is available globally.