GridGain Developers Hub
GitHub logo GridGain iso GridGain.com
GridGain Software Documentation

Migrating from Oracle Coherence

Overview

GridGain, in addition to the Data Grid functionality, also provides a feature rich in-memory compute grid, streaming functionality including event workflow and CEP sliding windows support, and a Hadoop Accelerator. However, since Oracle Coherence is mainly a Data Grid product, migration from Oracle Coherence to GridGain revolves largely around the Data Grid features.

This documentation provides main abstractions and concepts to ease the migration from Coherence to GridGain. For more specific use cases and examples, refer to the main GridGain Documentation.

EntryProcessor

A direct analogy for Coherence EntryProcessor is the invoke(…​) closure in GridGain. The invoke closure is invoked atomically regardless of any other concurrent updates or transactions. Similar to the Coherence EntryProcessor API, the GridGain invoke closure receives a previous entry value, potentially updates it, and returns a new value (user can also return null if the entry should be deleted from cache).

As an example, the following Coherence code uses EntryProcessor to increment an integer stored in cache:

Coherence

final NamedCache cache = CacheFactory.getCache("cacheName");
final String key = "key";

cache.put(key, new Integer(1));

cache.invoke(key, new MyCounterProcessor());

...
public static class MyCounterProcessor extends AbstractProcessor {
    public Object process(InvocableMap.Entry entry) {
        Integer i = (Integer) entry.getValue();

        entry.setValue(new Integer(i.intValue() + 1));

        return null;
    }
}

Here is the GridGain equivalent code example, which behaves identically to the above Coherence EntryProcessor example:

GridGain (Java 8)

IgniteCache<String, Integer> cache = ignite.cache("mycache");

// Invokes EntryProcessor for a given key sequentially.
cache.invoke("mykey", (entry, args) -> {
  Integer val = entry.getValue();

  entry.setValue(val == null ? 1 : val + 1);

  return null;
});

GridGain (Java 7)

IgniteCache<String, Integer> cache = ignite.jcache("mycache");

cache.invoke("mykey", new EntryProcessor<String, Integer, Void>() {
  @Override
  public Object process(MutableEntry<Integer, String> entry, Object... args) {
    Integer val = entry.getValue();

    entry.setValue(val == null ? 1 : val + 1);

    return null;
  }
});

Queries and Indexing

Both GridGain and Oracle Coherence provide a rich set of querying capabilities. Even though the APIs are different, they are easily translated into one another. The main difference between the query APIs is that GridGain supports standard SQL query syntax, while the Coherence query API is based on Java predicates,window=blank_.

In addition to basic scan querying capabilities, GridGain also provides the following data querying features not available in Oracle Coherence:

  • SQL queries, including support for distributed JOIN operations

  • SQL Field queries, allowing the retrieval of specific value fields instead of the whole object

  • MapReduce queries, allowing for preprocessing of the data on the data node side, instead of the client side, for better performance

Query Execution

The example below illustrates how to query a Person object based on the salary field in Coherence:

Coherence

// Java predicate.
Filter filter = new EqualsFilter("getSalary", salary);

for (Iterator iter = cache.entrySet(filter).iterator(); iter.hasNext();) {
    Map.Entry entry = (Map.Entry) iter.next();
    // Process entry.
}

Here is the same example in GridGain:

GridGain

IgniteCache<Long, Person> cache = ignite.cache("mycache");

SqlQuery sql = new SqlQuery(Person.class, "salary > ?");

// Find only persons earning more than 1,000.
try (QueryCursor<Entry<Long, Person>> cursor = cache.query(sql.setArgs(1000))) {
  for (Entry<Long, Person> e : cursor)
    System.out.println(e.getValue().toString());
}

Defining Indexes and Query Fields

This example creates an index in Oracle Coherence based on the salary field:

Coherence

NamedCache cache = CacheFactory.getCache("PersonCache");
ValueExtractor extractor = new ReflectionExtractor("getSalary");

cache.addIndex(extractor, true, null);

In GridGain, there are 3 ways to define indexes and fields available for querying. Indexes and queryable fields can be configured from code via the @QuerySqlField annotation. As shown in the example below, desired fields should be marked with this annotation.

GridGain (Annotations)

public class Person implements Serializable {
  /** Indexed field. Will be visible in SQL. */
  @QuerySqlField(index = true)
  private long id;

  /** Will be visible in SQL. */
  @QuerySqlField
  private Long salary;

  /** Will NOT be visible in SQL. */
  private int age;
}

Indexes and fields may also be configured with org.apache.ignite.cache.QueryEntity which is convenient for XML configuration using Spring.

GridGain (XML)

<bean class="org.apache.ignite.configuration.CacheConfiguration">
    <property name="name" value="mycache"/>
    <!-- Configure query entities -->
    <property name="queryEntities">
        <list>
            <bean class="org.apache.ignite.cache.QueryEntity">
                <property name="keyType" value="java.lang.Long"/>
                <property name="valueType" value="org.apache.ignite.examples.Person"/>

                <property name="fields">
                    <map>
                        <entry key="id" value="java.lang.Long"/>
                        <entry key="name" value="java.lang.String"/>
                    </map>
                </property>

                <property name="indexes">
                    <list>
                        <bean class="org.apache.ignite.cache.QueryIndex">
                            <constructor-arg value="id"/>
                        </bean>
                    </list>
                </property>
            </bean>
        </list>
    </property>
</bean>

Indexes can also be created using DDL:

GridGain (SQL)

CREATE INDEX person_idx ON PERSON (id);

Query Pagination

Both products support pagination of query results. The main difference is that GridGain handles pagination automatically, while Coherence leaves it up to the user to fetch the next page.

The Oracle Coherence example below executes the same query as before, but now in paginated form:

Coherence

int pageSize = 25;
Filter filter = new GreaterEqualsFilter("getSalary", salary);

// Get entries 1-25
Filter limitFilter = new LimitFilter(filter, pageSize);

Set entries = cache.entrySet(limitFilter);

// Iterate over result set.
...

// Get entries 26-50
limitFilter.nextPage();
entries = cache.entrySet(limitFilter);

// Iterate over result set.
...

In GridGain, Query abstract class represents an abstract paginated query to be executed on the distributed cache. You can set the page size for the returned cursor via the Query.setPageSize(…​) method (default is 1024).

Here is the GridGain code that executes the same paginated query as above:

GridGain

IgniteCache<AffinityKey<Long>, Person> cache = Ignition.ignite().cache(PERSON_CACHE);

// SQL clause which selects salaries based on range.
String sql = "salary > ? and salary <= ?";
SqlQuery query = new SqlQuery<AffinityKey<Long>, Person>(Person.class, sql).setArgs(0, 1000);

// Page size
query.setPageSize(25);

// Iterate over the entries
cache.query(query).iterator();

Explicit Locking

The Locking API is very similar between GridGain and Oracle Coherence.

Coherence

NamedCache cache = CacheFactory.getCache("txCache");
Object key = "example_key";
cache.lock(key, -1);

try {
    Object value = cache.get(key);
    // ...
    cache.put(key, value);
}
finally {
    cache.unlock(key);
}

Here is the equivalent GridGain locking code example, which is very similar to the Coherence example above:

GridGain

IgniteCache<String, Integer> cache = ignite.cache("myCache");

// Create a lock for the given key.
Lock lock = cache.lock("keyLock");
try {
    // Aquire the lock.
    lock.lock();

    cache.put("Hello", 11);
    cache.put("World", 22);
}
finally {
    // Release the lock.
    lock.unlock();
}

Transactions

Both GridGain and Coherence support the same transactional semantics. However, GridGain transactions are very fast (orders of magnitude faster than Coherence) and are recommended whenever transactional behavior is required, even if a transaction spans multiple partitions. Transactions are typically much slower in Coherence, and users are generally recommended to use explicit locks or EntryProcessor (which does not work across multiple partitions) instead.

Transactions in GridGain and Coherence also look similar from an API standpoint, however, the GridGain API is more compact.

Coherence

Connection con = new DefaultConnectionFactory().createConnection("txCache");

con.setAutoCommit(false);

try {
    OptimisticNamedCache cache = con.getNamedCache("MyTxCache");

    cache.insert(key1, value1);
    cache.insert(key2, value2);
    con.commit();
}
catch (Exception e) {
    con.rollback();
    throw e;
}
finally {
    con.close();
}

The GridGain example below uses Java auto-closable syntax to complete a transaction, and therefore results in more compact code.

GridGain

try (Transaction tx = transactions.txStart(OPTIMISTIC, REPEATABLE_READ)) {
    Integer hello = cache.get("Hello");

    if (hello == 1)
        cache.put("Hello", 11);

    cache.put("World", 22);

    tx.commit();
}

Learn more about GridGain transactions here.

Affinity Colocation

The Affinity Colocation feature provides the ability to colocate different keys together on the same physical JVM, allowing for efficient querying of different cache entries together. Both GridGain and Coherence support similar affinity colocation semantics with slightly different APIs.

Affinity Colocation Using Key Class

For simple cases, Coherence requires that a key implements the KeyAssociation interface in order to be colocated with another key.

Coherence

public class PersonKey implements KeyAssociation {
    // ...

    public Object getAssociatedKey() {
       return getOrganizationId();
    }

    // ...
}

GridGain requires that the @AffinityKeyMapped annotation be attached to the alternate affinity key field.

GridGain

public class PersonKey {
    // Person ID used to identify a person.
    private String personId;

    // Company ID which will be used for affinity.
    @AffinityKeyMapped
    private String companyId;
    ...
}

To learn more about collocating data in GridGain, see the Affinity colocation documentation.

Binary Objects

Both, GridGain and Coherence allow for dynamic structure changes and cross-platform interoperability between Java, .NET, and C++. GridGain also allows accessing data from other languages, such as Python, Node.JS, and PHP via thin clients.

In GridGain, a concept similar to Coherence’s Portable Objects is called BinaryObjects. GridGain serializes objects in a binary format and stores them in caches as BinaryObjects. There are several advantages to this binary format:

  • It enables you to read an arbitrary field from an object’s serialized form without full object deserialization. This ability completely removes the requirement to have the cache key and value classes deployed on the server node’s classpath.

  • It enables you to add and remove fields from objects of the same type. Given that server nodes do not have model classes definitions, this ability allows dynamic change to an objects structure, and even allows multiple clients with different versions of class definitions to co-exist.

  • It enables you to construct new objects based on a type name without having class definitions at all, hence allowing dynamic type creation.

Creating Binary Objects

One of the notorious issues with Coherence portable objects is that it requires extensive configuration for assigning portable IDs to every field. These IDs are hard to configure and are very hard to maintain, especially since they have to be unique across class hierarchies. GridGain, on the other hand, does not require any special handling for binary objects, and any existing Java, .NET, or C++ object can be treated as a Portable Object.

Here is an example of how to declare a portable object in Coherence:

Coherence

@Portable
public class Person {
   @PortableProperty(0)
   public String getFirstName() {
      return m_firstName;
   }

   private String m_firstName;

   @PortableProperty(1)
   private String m_lastName;

   @PortableProperty(2)
   private int m_age;
}

In GridGain, a binary object does not require any special annotations or configuration, so any existing object can become a binary object.

GridGain

// Any Java object can be used as a binary Object
public class Person {
   private String m_firstName;

   private String m_lastName;

   private int m_age;
}

Declaring Binary Objects

Coherence requires that portable objects are declared in the configuration file and also requires specifying and maintaining of unique type IDs for every portable object.

GridGain does not require any configuration for binary objects. However, it provides an option to change the default behavior. Read more on Binary Objects here.

Click here,window=blank_ for examples on how to use binary objects in GridGain.

Thin Clients

GridGain also provides Thin Clients - lightweight GridGain clients that connect to the cluster via a standard socket connection - for various languages. Thin clients are based on the binary client protocol making it possible to support Ignite connectivity from any programming language - Java, .NET, and C++, Python, Node.JS, and PHP.

Configuration

Unlike Coherence, which only supports proprietary XML configuration syntax, GridGain supports standard Spring XML configuration syntax. In addition to the XML configuration, GridGain allows configuring the grid directly from code.

For more information about GridGain configuration, refer to Understanding Configuration.