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.
int
type for event properties
Do not use 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.