[Functional Assets Required to Make Highly Available Nontrivial Distributed Systems] 


Methodology, components & tools > Design patterns > Process compound
 

In distributed systems, requests for different resources can be efficiently conducted by using a special trader service. A traders hierarchy can be maintained to make the service more powerful and flexible, with an homogeneous treatment from the client's perspective. This structure is basically a composite pattern (Gamma et al., 1995).

Declarative programming is very valuable when dealing with behaviors such as the composition of concurrent elements. For example, a synchronous (sequential) delegation in each component of a given composite group can be implemented using list comprehension. Thus, if call(Pid, M) sends message M to process Pid and then waits for a reply, the following behavior performs a sequential delegation in all child processes:

composite_behaviour(Request, State) -> Combine([call(S, Request) || S <- State#composite_state.children]).

The composite process implements the Request service as a sequential delegation of the service in all its children, and then combines the results using the Combine ([Response]) -> response function. It is important to realize that the server state, State, is represented as a record with a field named children. Using this abstraction, it is possible to define a lookup service to find the best candidate for a given task:

sequential_storage_behaviour({lookup, MO}, State) -> MinCost = State#group_state.min_cost, MinCost([call(S, {lookup, MO}) || S <- State#group_state.children]).

In the example, the function min_cost, stored as part of the state of the group, is used as a functional strategy to choose the best answer of all their children. This allows changing the selection algorithm at runtime.

There are many different types of interaction between the composite and its components. "Figure 5 shows two sequence diagrams describing the sequential and parallel common interactions. In parallel composition, the composite sends the request to all its components in parallel, and then receives the responses. This parallel delegation of a compound process is defined as:

group_behaviour(Request, State) -> Combine([ async_recv(Token) || Token <- [ async_call(S, Request) || S <- State#composite_state.children]]).

In this example, async_call(Pid, M) -> Token sends the message M and inmediately returns a token that can later on be used to obtain an answer calling async_recv(Token) -> Response. All answers are combined using function Combine to produce the composition response.




Fig. 5. Sequential and parallel interactions between a composite and its components
References
  • Gamma, E., Helm, R., Johnson, R., &Vlissides, J.(1995)Design patterns elements of reusable object-oriented softwareMassachusetts: Addison-Wesley