27 Using Live Events
This chapter includes the following sections:
- Overview of Live Events
Coherence provides an event programming model that allows extensibility within a cluster when performing operations against a data grid. - Understanding Live Event Types
Event types represent observable occurrences of cluster operations. - Handling Live Events
Applications handle live events using event interceptors.
Parent topic: Performing Data Grid Operations
Overview of Live Events
-
Partitioned Cache Events – A set of events that represent the operations being performed against a set of entries in a cache. Partitioned cache events include both entry events and entry processor events. Entry events are related to inserting, removing, and updating entries in a cache. Entry processor events are related to the execution of entry processors.
-
Partitioned Cache Lifecycle Events – A set of events that represent the operations for creating a cache, destroying a cache, and clearing all entries from a cache.
-
Partitioned Service Events – A set of events that represent the operations being performed by a partitioned service. Partitioned service events include both partition transfer events and partition transaction events. Partition transfer events are related to the movement of partitions among cluster members. Partition transaction events are related to changes that may span multiple caches and are performed within the context of a single request.
-
Lifecycle Events – A set of events that represent the activation and disposal of a
ConfigurableCacheFactory
instance. -
Federation Events – A set of events that represent the operations being performed by a federation service. Federation events include both federated connection events and federated change events. Federated connection events are related to the interaction of federated participants and federated change events are related to cache updates.
Applications create and register event interceptors to consume events. Event interceptors handle the events and implement any custom logic as required. Events have different rules which govern whether or not mutable actions may be performed upon receiving the event.
Parent topic: Using Live Events
Understanding Live Event Types
This section includes the following topics:
- Understanding Partitioned Cache Events
- Understanding Partitioned Cache Lifecycle Events
- Understanding Partitioned Service Events
- Understanding Lifecycle Events
- Understanding Federation Events
Parent topic: Using Live Events
Understanding Partitioned Cache Events
Partitioned cache events represent operations that are performed against a set of entries in a cache. Partitioned cache events include entry events (EntryEvent
) and entry processor events (EntryProcessorEvent
). These events are defined within the com.tangosol.net.events.partition.cache
package.
This section includes the following topics:
Parent topic: Understanding Live Event Types
Entry Events
Entry events represent operations for inserting, removing, and updating entries in a cache. Precommit entry events (INSERTING
, REMOVING
, and UPDATING
) are raised before the operation is performed to allow modification to an entry. The following holds true when modifying entries:
-
Event interceptors that are registered for precommit entry events are synchronously called.
-
A lock is held for each entry during the processing of the event to prevent concurrent updates.
-
Throwing an exception prevents the operation from being committed.
Postcommit entry events (INSERTED
, REMOVED
, and UPDATED
) are raised after an operation is performed and in the same order as the events occurred. Postcommit events indicate that an entry is no longer able to be modified. Event interceptors for postcommit entry events are asynchronously processed.
Table 27-1 lists the entry event types.
Table 27-1 Entry Events
Event Types | Description |
---|---|
|
Indicates that an entry (or multiple entries) is going to be inserted in the cache. |
|
Indicates that an entry (or multiple entries) has been inserted in the cache. |
|
Indicates that an entry (or multiple entries) is going to be removed from the cache. |
|
Indicates that an entry (or multiple entries) has been removed from the cache. |
|
Indicates that an entry (or multiple entries) is going to be updated in the cache. |
|
Indicates that an entry (or multiple entries) has been updated in the cache. |
Parent topic: Understanding Partitioned Cache Events
Entry Processor Events
Entry processor events represent the execution of an entry processor on a set of binary entries. Precommit entry processor events (EXECUTING
) are raised before an entry processor is executed to allow modification to the entry processor instance. The following holds true when modifying an entry processor:
-
Event interceptors that are registered for precommit entry processor events can modify the entries being processed and expect the modifications to be durable.
-
Event interceptors that are registered for precommit entry processor events are synchronously called.
-
Entry processors can be shared across threads; therefore, ensure thread safety when modifying an entry processor.
-
A lock is held for each entry during the processing of the event to prevent concurrent updates.
-
Throwing an exception prevents the entry processor from being executed.
Postcommit entry processor events (EXECUTED
) are raised after an entry processor is executed and in the same order that the events occurred. Postcommit events indicate that an entry is no longer able to be modified. Event interceptors for postcommit entry processor events are asynchronously processed.
Table 27-2 lists the entry processor event types.
Table 27-2 Entry Processor Events
Event Types | Description |
---|---|
|
Indicates that an entry processor is going to be executed on a set of entries. |
|
Indicates that an entry processor has been executed on a set of entries. |
Parent topic: Understanding Partitioned Cache Events
Understanding Partitioned Cache Lifecycle Events
Partitioned cache lifecycle events (CacheLifecycleEvent
) represent the creation, destruction, and truncation of a partitioned cache. These events are defined within the com.tangosol.net.events.partition.cache
package.
Partitioned cache lifecycle events are raised after a partitioned cache has been created, destroyed (NamedCache.destory
), or truncated (NamedCache.truncate)
. A TRUNCATED
event does not include the entries that are removed from a cache.
Table 27-3 Partitioned Cache Lifecycle Events
Event Types | Description |
---|---|
|
Indicates that storage for a given cache has been created. |
|
Indicates that storage for a given cache has been destroyed. |
|
Indicates that all mappings for a given cache have been removed from storage. |
Parent topic: Understanding Live Event Types
Understanding Partitioned Service Events
Partitioned service events represent operations being performed by a partitioned service. Partitioned service events include transfer events (TransferEvent
), transaction events (TransactionEvent
) and unsolicited commit events (UnsolicitedCommitEvent
). These events are defined within the com.tangosol.net.events.partition
package.
This section includes the following topics:
Parent topic: Understanding Live Event Types
Transfer Events
Partitioned service transfer events represent partition transfers between storage enabled members. The event includes the service name for which the transfer is taking place, the partition ID, the cluster members involved in the transfer and a map of cache names to entries. The entries cannot be modified.
Note:
Transfer events are raised while holding a lock on the partition being transferred that blocks any operations for the partition.
Table 27-4 lists the transition event types.
Table 27-4 Transition Events
Event Types | Description |
---|---|
|
Indicates that a set of entries are being transferred from the current member. |
|
Indicates that a set of entries has been transferred to or restored by the current member. |
Parent topic: Understanding Partitioned Service Events
Transaction Events
Partitioned service transaction events represent changes to binary entries (possibly from multiple caches) that are made in the context of a single service request. Precommit transaction events (COMMITTING
) are raised before any operations are performed to allow modification to the entries. The following holds true when modifying entries:
-
A lock is held for each entry during the processing of the event to prevent concurrent updates.
-
Throwing an exception prevents the operation from being committed.
Postcommit transaction events (COMMITTED
) are raised after an operation is performed. Postcommit events indicate that the entries are no longer able to be modified.
Table 27-5 lists the transaction event types.
Table 27-5 Transaction Events
Event Types | Description |
---|---|
|
Indicates that entries are going to be inserted in their respective cache. |
|
Indicates that entries have been inserted in their respective cache. |
Parent topic: Understanding Partitioned Service Events
Unsolicited Commit Events
Partitioned service unsolicited commit events represent changes pertaining to all observed mutations performed against caches that were not directly caused (solicited) by the partitioned service. These events may be due to changes made internally by the backing map, such as eviction, or references to the backing map that caused changes. The event contains all modified entries which may span multiple backing maps. The entries cannot be modified.
Table 27-6 lists the unsolicited commit event types.
Table 27-6 Transaction Events
Event Types | Description |
---|---|
|
Indicates that entries have been inserted in their respective cache. |
Parent topic: Understanding Partitioned Service Events
Understanding Lifecycle Events
Lifecycle events (LifecycleEvent
) represent actions that occur on a ConfigurableCacheFactory
instance. These events are defined within the com.tangosol.net.events.application
package.
An ACTIVATED
event is raised after all services that are associated with a cache factory are started. The services are defined in the cache configuration file and must be configured to autostart. A DISPOSING
event is raised before all services are shut down and any resources are reclaimed. Event interceptors that handle a DISPOSING
event are notified before the services are shutdown. A ConfigurableCacheFactory
instance can only be activated and disposed of once.
Note:
Lifecycle events are dispatched to event interceptors by the same thread calling the lifecycle methods on the ConfigurableCacheFactory
implementation. This thread may be synchronized. Event interceptors must ensure that any spawned threads do not synchronize on the same ConfigurableCacheFactory
object.
Table 27-7 lists the lifecycle event types.
Table 27-7 Lifecycle Events
Event Types | Description |
---|---|
|
Indicates that a |
|
Indicates that a |
Parent topic: Understanding Live Event Types
Understanding Federation Events
Federation events represent a set of events that represent the operations being performed by a federation service. Federation events include federated connection events (FederatedConnectionEvent
), federated change events (FederatedChangeEvent
), and federated partition events (FederatedPartitionEvent
) . These events are defined within the com.tangosol.net.events.federation
package.
This section includes the following topics:
Parent topic: Understanding Live Event Types
Federated Connection Events
Federated connection events represent the communication between participants of a federated service.
Note:
Federated connection events are raised on the same thread that caused the event. Interceptors that handle these events must never perform blocking operations.
Table 27-8 lists the federated connection event types.
Table 27-8 Federated Connection Events
Event Types | Description |
---|---|
|
Indicates that a connection is about to be initiated to a participant. |
|
Indicates that participant is no longer connected. |
|
Indicates that a participant is backlogged. If the participant is remote, then it indicates the remote participant has more work than it can handle. If the participant is local, then it indicates this participant has more work than it can handle. |
|
Indicates that a participant is no longer backlogged. |
|
Indicates that an error occurred while applying changes on the remote participant or that the maximum connection retry has been reached. |
Parent topic: Understanding Federation Events
Federated Change Events
Federated change events represent a transactional view of the changes that occur on the local cluster participant in a federation. The transaction is for a partition; that is, all changes that belong to a single partition are captured in a single FederatedChangeEvent
object.
Note:
Synthetic operations are not included in federation change events.
Federated change events allow conflict resolution by allowing or disallowing changes to cache entries before they are committed. See Processing Federated Change Events in Administering Oracle Coherence.
The following holds true when modifying entries:
-
A lock is held for each entry during the processing of the event to prevent concurrent updates.
-
Throwing an exception prevents the operation from being committed.
Table 27-9 lists the federated change event types.
Table 27-9 Federated Change Events
Event Types | Description |
---|---|
|
Indicates that entries are going to be inserted in the cache on the local participant. |
|
Indicates that entries from other participants are going to be inserted in the cache on the local participant. |
|
Indicates that entries are going to be replicated to remote participants. |
Parent topic: Understanding Federation Events
Federated Partition Events
Federated partition events represent a change in the federation state of a partition during federation.
Note:
This event is dispatched for each partition.
Table 27-10 lists the federated partition event types.
Table 27-10 Federated Partition Events
Event Types | Description |
---|---|
|
Indicates that a partition is being federated to a destination as a result of a |
|
Indicates that partition federation to a destination is complete as a result of a |
Parent topic: Understanding Federation Events
Handling Live Events
This section includes the following topics:
- Creating Event Interceptors
- Understanding Event Threading
- Registering Event Interceptors
- Chaining Event Interceptors
Parent topic: Using Live Events
Creating Event Interceptors
Event interceptors are created by implementing the EventInterceptor
interface. The interface is defined using generics and allows you to subscribe to events by specifying the generic type of the event as a type parameter. The inherited onEvent
method provides the ability to perform any necessary processing upon receiving an event. The following example demonstrates subscribing to all transfer events and is taken from Example 27-1:
public class RedistributionInterceptor implements EventInterceptor<TransferEvent> public void onEvent(TransferEvent event) { ...
The @Interceptor
annotation can be used to further restrict the events to specific event types and also provides further configuration of the interceptor. The following example defines an interceptor identifier and restricts the events to only transfer DEPARTING
events:
@Interceptor(identifier = "redist", transferEvents = TransferEvent.Type.DEPARTING) public class RedistributionInterceptor implements EventInterceptor<TransferEvent> public void onEvent(TransferEvent event) { ...
The @Interceptor
annotation includes the following attributes:
-
identifier
– Specifies a unique identifier for the interceptor. The identifier can be overridden when registering an interceptor class in the cache configuration file. This attribute is optional. A unique name is automatically generated by the event infrastructure if the attribute is omitted. -
entryEvents
– Specifies an array of entry event types to which the interceptor wants to subscribe. -
entryProcessorEvents
– Specifies an array of entry processor event types to which the interceptor wants to subscribe. -
transferEvents
– Specifies an array of transfer event types to which the interceptor wants to subscribe. -
transactionEvents
– Specifies an array of transaction event types to which the interceptor wants to subscribe. -
unsolicitedEvents
– Specifies an array of unsolicited event types to which the interceptor wants to subscribe. -
cacheLifecycleEvent
– Specifies an array of cache lifecycle event types to which the interceptor wants to subscribe. -
federatedChangeEvents
– Specifies an array of federation change event types to which the interceptor wants to subscribe. -
federatedConnectionEvent
– Specifies an array of federation connection event types to which the interceptor wants to subscribe. -
order
– Specifies whether the interceptor is placed at the front of a chain of interceptors. See Chaining Event Interceptors. The legal values areLOW
andHIGH
. A value ofHIGH
indicates that the interceptor is placed at the front of the chain of interceptors. A value ofLOW
indicates no order preference. The default value isLOW
. The order can be overridden when registering an interceptor class in the cache configuration file.
The following example demonstrates a basic event interceptor implementation that subscribes to all transfer event types (both DEPARTING
and ARRIVED
). The onEvent
method simply logs the events to show partition activity. The example is part of the Coherence examples.
Note:
Event instances are immutable and their lifecycle is controlled by the underlying system. References to event classes must not be held across multiple invocations of the onEvent()
method.
Example 27-1 Example Event Interceptor Implementation
package com.tangosol.examples.events; import com.tangosol.net.CacheFactory; import com.tangosol.net.events.EventInterceptor; import com.tangosol.net.events.annotation.Interceptor; import com.tangosol.net.events.partition.TransferEvent; @Interceptor(identifier = "redist") public class RedistributionInterceptor implements EventInterceptor<TransferEvent> { public void onEvent(TransferEvent event) { CacheFactory.log(String.format("Discovered event %s for partition-id %d from remote member %s\n", event.getType(), event.getPartitionId(), event.getRemoteMember()), CacheFactory.LOG_INFO); } }
Parent topic: Handling Live Events
Understanding Event Threading
Event interceptors can have a significant impact on cache operations and must be careful not to block or otherwise affect any underlying threads. The impact for both precommit event types and postcommit event types should be carefully considered when creating event interceptors.
Note:
EventInterceptor
instances can be reused; however, they should be immutable or thread-safe so that they can be dispatched by multiple threads concurrently.
Precommit Events
Precommit event types allow event interceptors to modify entries before the entries are committed to a cache. The interceptors are processed synchronously and must not perform long running operations (such as database access) that could potentially block or slow cache operations. Calls to external resource must always return as quickly as possible to avoid blocking cache operations.
The dynamic thread pool is automatically enabled for partitioned services. The thread pool creates additional threads to process cache operations and helps alleviate the overall impact of event interceptors that handle precommit events, but the potential for blocking still exists. Use the <thread-count-min>
and <thread-count-max>
elements within a distributed cache definition to explicitly set the size of the thread pool. See Table B-19.
Postcommit Events
Postcommit events do not allow an event interceptor to modify entries. The events are raised in the same order as the events occurred and the interceptors are processed asynchronously. Event interceptors that perform long running operations can cause a backlog of requests that could ultimately affect performance. It is a best practice to use the Java Executor service to perform such operations on a separate thread.
Parent topic: Handling Live Events
Registering Event Interceptors
Event interceptors are registered within a cache configuration file. Event interceptors are registered either for a specific cache or for a partitioned service. Event interceptor that are registered for a specific cache only receives events that pertain to that cache. Event interceptors that are registered for a partitioned service receive events for all caches that are managed by the service.
Note:
Event interceptors for service-level events (such as transfer events, transaction events, and federation events) must be registered for a partition service and cannot be restricted to a specific cache.
This section includes the following topics:
- Registering Event Interceptors For a Specific Cache
- Registering Event Interceptors For a Partitioned Service
- Registering Event Interceptors For a Cache Configuration Factory
- Using Custom Registration
- Guidelines for Registering Event Interceptors
Parent topic: Handling Live Events
Registering Event Interceptors For a Specific Cache
To register interceptors on a specific cache, include an <interceptors>
element, within a <cache-mapping>
element, that includes any number of <interceptor>
subelements. Each <interceptor>
element must include an <instance>
subelement and provide a fully qualified class name that implements the EventInterceptor
interface. See interceptor element. The following example registers and event interceptor class called MyInterceptor
.
Note:
Registering interceptors in a cache map is intended only for cache-level events and not for service-level events. If you register service-level event interceptors in a cache map, then the interceptor is triggered on all caches that belong to the service.
<caching-scheme-mapping> <cache-mapping> <cache-name>example</cache-name> <scheme-name>distributed</scheme-name> <interceptors> <interceptor> <name>MyInterceptor</name> <instance> <class-name> com.tangosol.examples.events.MyInterceptor </class-name> </instance> </interceptor> </interceptors> </cache-mapping> </caching-scheme-mapping>
Parent topic: Registering Event Interceptors
Registering Event Interceptors For a Partitioned Service
To register interceptors on a partitioned service, include an <interceptors>
element, within a <distributed-scheme>
element, that includes any number of <interceptor>
subelements. Each <interceptor>
element must include an <instance>
subelement and provide a fully qualified class name that implements the EventInterceptor
interface. See interceptor. The following example registers the RedistributionInterceptor
class defined in Example 27-1.
<distributed-scheme> <scheme-name>distributed</scheme-name> <service-name>PartitionedService1</service-name> <backing-map-scheme> <local-scheme/> </backing-map-scheme> <autostart>true</autostart> <interceptors> <interceptor> <name>MyInterceptor</name> <instance> <class-name> com.tangosol.examples.events.RedistributionInterceptor </class-name> </instance> </interceptor> </interceptors> </distributed-scheme>
Parent topic: Registering Event Interceptors
Registering Event Interceptors For a Cache Configuration Factory
To register interceptors on a ConfigurableCacheFactory
instance, include an <interceptors>
element, within a <cache-config>
element, that includes any number of <interceptor>
subelements. Each <interceptor>
element must include an <instance>
subelement and provide a fully qualified class name that implements the EventInterceptor
interface. See interceptor. The following example registers and event interceptor class called MyInterceptor
.
<cache-config> <interceptors> <interceptor> <name>MyInterceptor</name> <instance> <class-name>com.tangosol.examples.events.MyInterceptor</class-name> </instance> </interceptor> </interceptors> ... </cache-config>
Parent topic: Registering Event Interceptors
Using Custom Registration
The @Interceptor
annotation and generic types are used by the event infrastructure to register event interceptors with the appropriate event dispatcher. This mechanism is acceptable for most uses cases. However, for advanced use cases, an event interceptor can choose to implement the EventDispatcherAwareInterceptor
interface and manually register an event interceptor with the required event dispatcher.
The introduceEventDispatcher
method includes the event dispatcher to which the interceptor will be registered. The methods on the dispatcher are then used to add and remove interceptors, restrict specific event types, and configure the interceptor as required. The following example shows a custom implementation that explicitly registers an interceptor, subscribes to entry INSERTING
events, and configures ordering to ensure that the interceptor is called and notified first:
public void introduceEventDispatcher(String sIdentifier, EventDispatcher dispatcher) { dispatcher.addEventInterceptor(sIdentifier, this, new HashSet(Arrays.asList(EntryEvent.Type.INSERTING)), true); }
Note:
If an interceptor is configured without using the annotations, then the configuration cannot be overridden using the cache configuration file.
Interceptors can also be programmatically registered using the InterceptorRegistry
API. Registering an interceptor causes it to be introduced to all currently registered and future event dispatchers. An interceptor can determine whether or not to bind to a dispatcher by using the introduceEventDispatcher
method as shown in the previous example.
The InterceptorRegistry
API is available from the ConfigurableCacheFactory
interface and is called using the getInterceptorRegistry
method. The API can be used together with the cache configuration file when declaratively registering interceptors. The API is often used with custom DefaultCacheServer
implementations to add interceptors programmatically, or the API is used to selectively register interceptors when using the InvocationService
interface. The following example demonstrates registering an interceptor.
CacheFactory.getConfigurableCacheFactory().getInterceptorRegistry() .registerEventInterceptor(new MyEventIntercepor());
Parent topic: Registering Event Interceptors
Guidelines for Registering Event Interceptors
Interceptors can be registered in multiple distributed schemes for the same service. In addition, interceptor classes can be inherited if a distributed scheme uses scheme references. In both cases, the interceptor classes are registered with the service.
For most cases, registering multiple interceptor classes is not an issue. However, there are increased chances for duplicating the same interceptor classes and identifier names for a given service. The following guidelines should be followed to ensure registration errors do not occur because of duplication:
-
An interceptor class can be duplicated multiple time in a distributed scheme or in multiple schemes of the same service as long as the identifier names are unique or no identifier name is defined. For the later, a unique name is automatically generated by the event infrastructure.
-
An interceptor class (duplicated or not) with the same identifier name cannot be registered multiple times in a distributed scheme and results in a registration error.
-
An interceptor class that is inherited by scheme reference is registered once for each distinct service name in the reference chain.
Parent topic: Registering Event Interceptors
Chaining Event Interceptors
This section includes the following topics:
Parent topic: Handling Live Events
Overview of Chaining Event Interceptors
Event interceptors that are registered for the same event type are serially called by the thread responsible for dispatching an event. The ability to chain interceptors in this manner allows complex processing scenarios where custom logic is executed based on the outcome of other interceptors in the chain. Each event interceptor in a chain can:
-
Modify data associated with the event if allowed. For example, precommit operations such as
INSERTING
,UPDATING
, andREMOVING
entry events allow data modifications. -
Deny a precommit operations by throwing an exception.
-
Enlist a new entry into a partition-level transaction.
-
Observe the results of any side effects caused by event interceptors further down the chain. If the interceptor chain is associated with a precommitted storage event, the ability to observe the results provides a second opportunity to deny the processing.
Observing the side effects of downstream event interceptors is accomplished using the
Event.nextInterceptor
method. When this method returns, it signifies that all downstream event interceptors executed and any changes to the state associated with the event can be inspected. TheEvent
object holds state on the processing of the event. The event calls each event interceptor'sonEvent
method as long as there are still event interceptors in the chain. If the event interceptor itself calls theEvent.nextInterceptor
method, it triggers the next event interceptor in the chain to execute. If the event interceptor returns, the event itself triggers the next event interceptor in the chain'sonEvent
method. Either way, all event interceptors are executed, but those that choose to call thenextInterceptor
method have the option of taking action based on the side effects that are observed.
Parent topic: Chaining Event Interceptors
Specifying an Event Interceptor Chain Order
Event interceptors in a chain are executed based on the order in which they are registered. Use the order
attribute on the @Interceptor
annotation to specify whether an interceptor is placed in the front of the chain. A value of HIGH
places the interceptor at the front of the chain. A value of LOW
indicates no order preference and is the default value. For example:
@Interceptor(identifier = "MyInterceptor", entryEvents = {Type.INSERTING, Type.INSERTED} order = Order.HIGH) public class MyInterceptor implements EventInterceptor<EntryEvent<?,?>> ...
The order can also be specified declaratively when registering an event interceptor in the cache configuration file and overrides the order
attribute that is specified in an event interceptor class.
To specify the ordering of interceptors in the cache configuration file, include an <order>
element, within an <interceptor>
element, that is set to HIGH
or LOW
. For example:
<interceptors> <interceptor> <name>MyInterceptor</name> <order>HIGH</order> <instance> <class-name>package.MyInterceptor</class-name> </instance> </interceptor> <interceptor> <name>MySecondInterceptor</name> <instance> <class-name>package.MySecondInterceptor</class-name> </instance> </interceptor> </interceptors>
Parent topic: Chaining Event Interceptors