Oct 29

The classic producer-consumer pattern makes a few assumptions in order to work. In this post I’ll discuss some of these assumptions, what happens when they break, and a cool solution to deal with it.
The two assumptions I’d like to discuss are:

  1. All producers are equal – this means that all producers are limited by one, shared limit – the mediator queue between them and the consumers. This is a great assumption for most implementations: usually you are limited by the amount of work your consumers can handle and usually those are limited by the amount of processors you would physically have, therefore it doesn’t matter if the piece of work came from producer X or producer Y – either way you’d still have the same amount of processors.
  2. Once sent to the queue, a producer doesn’t care about its products – this means that there is no link between the produced work and the producer once it has been sent to the mediation queue. And why should it? Its task was to create the work, afterwards its the consumer’s job to deal with the work, and then it would be the garbage collector’s task to reclaimed the memory space that work used. If the producer kept a link to the work, it couldn’t have been reclaimed without proper notifications between the producer and the consumer – and that would be completely against the decoupling nature of the producer-consumer pattern.

So far you might be thinking “these are good, based assumptions; why should they ever break?”. Well, keep on reading then. Continue reading »

Share
Dec 07

One of the most classic patterns in software is the producer-consumer pattern. There is a module producing data, and a module reading it for further processing. Moreover, in order to achieve better performance, usually there are many consumer modules running on many different threads while the producer (or several producers) run on its own thread. This allows to distribute processing work between threads, and in the multi-core, multi-processor environment of today, between physical processors as well. Concurrent frameworks (such as java.util.concurrent) provide out-of-the-box solutions for these kind of problems.

Same pattern, multiple machines

This pattern also works well when wanting to distribute tasks between different physical machines as well. The producer machine somehow creates information, and offloads the task of processing that information to other machines. Since we want to distribute the work between different machines without coupling issues, the data is written to a physical disk and using some sort of a distribution system (e.g. JMS queues), a notification containing the full path to the data is sent. An available consumer receiving the notification can then read the data from the physical disk and process it. The following diagram illustrates this simple idea:

Continue reading »

Share
Feb 26

Functors are defined as a function encapsulated in an object. In most cases, the function’s parameters can be set and the result retrieved using the common accessor pattern, such as setParameter1(Object value) or getResult(). The fact that these functions are encapsulated by real objects is also the reason for its greatest benefit: the use of many design patterns including structural ones such as the Decorator pattern and behavioral ones such as the Visitor pattern.

Functors in software design

While the classic object oriented design encourages inheritance and method overloading to achieve code reuse and code specialization, the functors’ design promote another approach, of composition. Functors are created to accomplish simple tasks and are later connected together, by connecting one functor’s result to another’s parameter, to satisfy a greater goal of the application.

Continue reading »

Share