Coherence is architected as a collection of services. Each Coherence service consists of the Coherence code that implements the service, along with an associated configuration. The service runs on an allocated pool of threads with associated queues that receive requests and return responses.
Coherence does not support re-entrant calls. A "re-entrant service call" occurs when a service thread, in the act of processing a request, makes a request to that same service. As all requests to a service are delivered by using the inbound queue, and Coherence uses a thread-per-request model, this means that each reentrant request would consume an additional thread (the calling thread would block while awaiting a response). Note that this is distinct from the similar-sounding concept of recursion.
A service is defined as a unique combination of a service name and a service type (such as Invocation, Replicated, or Distributed). For example, you can call from a distributed service
Dist-Customers into a distributed service named
Dist-Inventory, or from a distributed service named
Dist-Customers into a replicated service named
Repl-Catalog. Service names are configured in the cache configuration file using the
In the current implementation of Coherence, it is irrelevant whether the "call" is local or remote. This complicates the use of key association to support the efficient assembly of parent-child relationships. If you use key association to co-locate a Parent object with all of its Child objects, then you cannot send an EntryProcessor to the parent object and have that
EntryProcessor "grab" the (local) Child objects. This is true even though the Child objects are already in-process.
To access both a parent object and its child objects, you can do any of the following:
Embed the child objects within the parent object (using an "aggregate" pattern) or,
Use direct access to the server-side backing map (which requires advanced knowledge to do safely), or
Run the logic on another service (for example, Invocation targeted by using
PartitionedService.getKeyOwner), and have that service access the data by using
NamedCache interfaces, or
Place the child objects on another service which would allow reentrant calls (but incur network access since there is no affinity between partitions in different cache services).
Using the aggregate pattern is probably the best solution for most use cases. However, if this is impractical (due to size restrictions, for example), and there is a need to access both the parent and child objects without using a client/server model, the Invocation service approach is probably the best compromise for most use cases.
Even when re-entrancy is allowed, one should be very careful to avoid saturating the thread pool and causing catastrophic deadlock. For example, if service A calls service B, and service B calls service A, there is a possibility that a sufficient number of concurrent calls could fill one of the thread pools, which would cause a form of deadlock. As with traditional locking, using ordered access (for example, service A can call service B, but not vice versa) can help.
Service A calling into service A is never allowed
Service A calling into service B, and service B calling back into service A is technically allowed but is deadlock-prone and should be avoided if at all possible.
Service A calling into service B, and service B calling into service C, and service C calling back into service A is similarly restricted
Service A calling into service B is allowed
Service A calling into service B, and service B calling into service C, and service A calling into service C is similarly allowed
A service thread is defined as any thread involved in fulfilling a Coherence API request. Service threads may invoke any of the following entities:
Custom Serialization/Deserialization such as
Backing Map Listeners
Query logic such as
These entities should never make re-entrant calls back into their own services.