32 Constraints on Re-entrant Calls

This chapter describes the constraints on service re-entrant calls and provides general guidelines for making calls between service threads.

This chapter includes the following sections:

32.1 Overview of Constraints on Re-Entrant Calls

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, 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.

The Coherence architecture is based on 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.

32.2 Re-entrancy, Services, and Service Threads

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 <service-name> element.

32.2.1 Parent-Child Object Relationships

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 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 requirement 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.

32.2.2 Avoiding Deadlock

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 enough concurrent calls could fill a thread pool, 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.

So:

  • 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:

  • Map Listeners

  • Membership Listeners

  • Custom Serialization/Deserialization such as ExternalizableLite implementations

  • Backing Map Listeners

  • CacheLoader/CacheStore Modules

  • Query logic such as Aggregators, Filters, ValueExtractors and Comparators

  • Entry Processors

  • Triggers

  • InvocationService Invocables

These entities should never make re-entrant calls back into their own services.

32.3 Re-entrancy and Listeners

Membership listeners can observe the active set of members participating in the cluster or a specific service. Membership listener threading can be complex; thus, re-entrant calls from a member listener to any Coherence service should be avoided.