Get Started

Get Started

These guides demonstrate how to get started quickly with Hazelcast IMDG and Hazelcast Jet.

Hazelcast IMDG

Learn how to store and retrieve data from a distributed key-value store using Hazelcast IMDG. In this guide you’ll learn how to:

  • Create a cluster of 3 members.
  • Start Hazelcast Management Center
  • Add data to the cluster using a sample client in the language of your choice
  • Add and remove some cluster members to demonstrate data balancing capabilities of Hazelcast

Hazelcast Jet

Learn how to build a distributed data processing pipeline in Java using Hazelcast Jet. In this guide you’ll learn how to:

  • Install Hazelcast Jet and form a cluster on your computer
  • Build a simple pipeline that receives a stream of data, does some calculations and outputs some results
  • Submit the pipeline as a job to the cluster and observe the results
  • Scale the cluster up and down while the job is still running

An Easy Performance Improvement with EntryProcessor

February 10, 2020
An Easy Performance Improvement with EntryProcessor

A lot of a developer’s work is about transforming and aggregating data:

  • Increasing the quantity of a product in a shopping cart
  • Applying VAT on the price of a product
  • Computing the price of a shopping cart
  • Etc…

Sometimes, one needs the features of a full-fledged stream processing engine, such as Hazelcast Jet, sometimes not. For this post, let’s use Hazelcast IMDG to store the content of an e-commerce cart, just like we did in our recent blog post.

Straightforward Implementation

Specifically, when the user increases the quantity, we need to update the value contained in the relevant IMap. The implementation code looks like the following:

IMap<Long, Integer> cart = hazelcast.getMap("cart");
Integer quantity = cart.get(productId);
Integer newQuantity = quantity + 1;
cart.set(productId, newQuantity);

This snippet:

  1. Gets the data from the IMap
  2. Updates it
  3. And puts the new value in the IMap

Yet, there’s one issue. The API abstracts away the nitty-gritty details of the nature of Hazelcast IMDG: it’s distributed. When two or more Hazelcast nodes are started, they will by default broadcast to find each other, and form a cluster. Among other things, that allows data to be duplicated and backed up on more than one node. Thus, if one node fails, data is still available on another one. In the above example, nothing is said about the number of underlying nodes.

Now, imagine the above code runs on a specific node A. The cart is stored on another one B. Execution requires 2 network round trips:

  1. One to move the data from B to A
  2. Another to put it back from A to B

Sequence diagram of code not using EntryProcessor

This is highly inefficient, especially in the context of high-performance requirements scenarios, such as e-commerce.

EntryProcessor to the Rescue

Enter the EntryProcessor interface. From the JavaDoc:

An EntryProcessor passes you a Map.Entry. At the time you receive it the entry is locked and not released until the EntryProcessor completes. This obviates the need to explicitly lock as would be required with a ExecutorService.

Performance can be very high as the data is not moved off the Member partition. This avoids network cost and, if the storage format is InMemoryFormat.OBJECT, then there is no de-serialization or serialization cost.

EntryProcessors execute on the partition thread in a member.

Obviously, this is the solution. Migrating from the above code to an EntryProcessor is very straightforward:

IMap<Long, Integer> cart = hazelcast.getMap("default");
cart.executeOnKey(productId, new AbstractEntryProcessor<Long, Integer>() {
    @Override
    public Object process(Map.Entry<Long, Integer> entry) {
        Integer quantity = entry.getValue();
        Integer newQuantity = quantity + 1;
        entry.setValue(newQuantity);
        return null;
    }
});

Sequence diagram using EntryProcessor

Note that the entry’s value needs to be set in all cases (even if the entry’s value is mutable). The reason is that Hazelcast must be aware that the value has changed in order to propagate the new value to other backup nodes if necessary.

Finally, the entry processor can return any arbitrary object. In our use case, we can return the new quantity. This can be used to display it for example:

IMap<Long, Integer> cart = hazelcast.getMap("default");
Integer newQuantity = (Integer) cart.executeOnKey(productId, new AbstractEntryProcessor<Long, Integer>() {
    @Override
    public Object process(Map.Entry<Long, Integer> entry) {
        Integer quantity = entry.getValue();
        Integer newQuantity = quantity + 1;
        entry.setValue(newQuantity);
        return newQuantity;
    }
});

Conclusion

In this focused post, we learned about the EntryProcessor interface. Instead of getting the data locally and putting it back, it allows sending the computation on the cluster to be executed on the required node(s). It’s easy to set up, there’s no reason not to use it! If you want an instant performance improvement, you should consider using it.

Tags

About the Author

About the Author

Nicolas Frankel

Nicolas Frankel

Developer Advocate, Hazelcast

Nicolas Fränkel is a Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Currently working for Hazelcast. Also double as a teacher in universities and higher education schools, a trainer and triples as a book author.

Follow me on

Latest Blogs

An Experiment in Streaming: Bytecode Continuous Deployment

An Experiment in Streaming: Bytecode Continuous Deployment

Persisting In-Memory Data for Later Usage

View all blogs by the author
Open Gitter Chat