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

Affinity Collocation (Partition Pruning)

In many cases it is beneficial to collocate different entries if they are often accessed together. In this way, multi-entry queries will be executed on one node (where the objects are stored). This concept is known as affinity collocation.

Entries are assigned to partitions by the affinity function. The objects that have the same affinity keys will go to the same partitions. This allows you to design your data model in such a way that related entries are stored together. "Related" here refers to the objects that are in a parent-child relationship or objects that are often queried together.

For example, let’s say you have Person and Company objects, and each person has the companyId field that indicates the company the person works for. By specifying the Person.companyId and Company.ID as affinity keys, you ensure that all the persons working for the same company will be stored on the same node, where the company object is stored as well. Queries that request persons working for a specific company will be processed on a single node.

You can also collocate a computation task with the data. See Collocating Computations With Data.

Configuring Affinity Key

If you do not specify the affinity key explicitly, the cache key will be used as the default affinity key. If you create your caches as SQL tables using SQL statemens, the PRIMARY KEY will be the default affinity key.

If you want to colocate data from two caches by a different field, you have to use a complex object as the key. That object usually contains a field that uniquely identifies the object in that cache and a field that you want to use for collocation.

There are several ways to configure a custom affinity field within the custom key, which are described below.

The following example illustrates how you can collocate the person objects with the company objects using a custom key class and the @AffinityKeyMapped annotation.

public class AffinityCollocationExample {

    static class Person {
        private int id;
        private String companyId;
        private String name;

        public Person(int id, String companyId, String name) {
            this.id = id;
            this.companyId = companyId;
            this.name = name;
        }

        public int getId() {
            return id;
        }
    }

    static class PersonKey {
        private int id;

        @AffinityKeyMapped
        private String companyId;

        public PersonKey(int id, String companyId) {
            this.id = id;
            this.companyId = companyId;
        }
    }

    static class Company {
        private String id;
        private String name;

        public Company(String id, String name) {
            this.id = id;
            this.name = name;
        }

        public String getId() {
            return id;
        }
    }

    public void configureAffinityKeyWithAnnotation() {
        CacheConfiguration<PersonKey, Person> personCfg = new CacheConfiguration<PersonKey, Person>("persons");
        personCfg.setBackups(1);

        CacheConfiguration<String, Company> companyCfg = new CacheConfiguration<>("companies");
        companyCfg.setBackups(1);

        try (Ignite ignite = Ignition.start()) {
            IgniteCache<PersonKey, Person> personCache = ignite.getOrCreateCache(personCfg);
            IgniteCache<String, Company> companyCache = ignite.getOrCreateCache(companyCfg);

            Company c1 = new Company("company1", "My company");
            Person p1 = new Person(1, c1.getId(), "John");

            // Both the p1 and c1 objects will be cached on the same node
            personCache.put(new PersonKey(p1.getId(), c1.getId()), p1);
            companyCache.put("company1", c1);

            // get the person object
            p1 = personCache.get(new PersonKey(1, "company1"));
        }
    }
}
class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int CityId { get; set; }
    public string Companyid { get; set; }
}

class PersonKey
{
    public int Id { get; set; }

    [AffinityKeyMapped] public string CompanyId { get; set; }
}

class Company
{
    public string Name { get; set; }
}

class AffinityCollocation
{
    public static void Example()
    {
        var personCfg = new CacheConfiguration
        {
            Name = "persons",
            Backups = 1,
            CacheMode = CacheMode.Partitioned
        };

        var companyCfg = new CacheConfiguration
        {
            Name = "companies",
            Backups = 1,
            CacheMode = CacheMode.Partitioned
        };

        using (var ignite = Ignition.Start())
        {
            var personCache = ignite.GetOrCreateCache<PersonKey, Person>(personCfg);
            var companyCache = ignite.GetOrCreateCache<String, Company>(companyCfg);

            var person = new Person {Name = "Vasya"};

            var company = new Company {Name = "Company1"};

            personCache.Put(new PersonKey {Id = 1, CompanyId = "company1_key"}, person);
            companyCache.Put("company1_key", company);
        }
    }
}
CREATE TABLE IF NOT EXISTS Person (
  id int,
  city_id int,
  name varchar,
  company_id varchar,
  PRIMARY KEY (id, city_id)
) WITH "template=partitioned,backups=1,affinity_key=company_id";

CREATE TABLE IF NOT EXISTS Company (
  id int,
  name varchar,
  PRIMARY KEY (id)
) WITH "template=partitioned,backups=1";

An alternative way of configuring The affinity key field can also be configured in the cache configuration using the CacheKeyConfiguration class.

public void configureAffinityKeyWithCacheKeyConfiguration() {
    CacheConfiguration<PersonKey, Person> personCfg = new CacheConfiguration<PersonKey, Person>("persons");
    personCfg.setBackups(1);

    //configure the affinity key
    personCfg.setKeyConfiguration(new CacheKeyConfiguration("Person", "companyId"));

    CacheConfiguration<String, Company> companyCfg = new CacheConfiguration<String, Company>("companies");
    companyCfg.setBackups(1);

    Ignite ignite = Ignition.start();

    IgniteCache<PersonKey, Person> personCache = ignite.getOrCreateCache(personCfg);
    IgniteCache<String, Company> companyCache = ignite.getOrCreateCache(companyCfg);

    Company c1 = new Company("company1", "My company");
    Person p1 = new Person(1, c1.getId(), "John");

    // Both the p1 and c1 objects will be cached on the same node
    personCache.put(new PersonKey(1, c1.getId()), p1);
    companyCache.put(c1.getId(), c1);

    // get the person object
    p1 = personCache.get(new PersonKey(1, "company1"));
}

Instead of defining a custom key class, you can use the AffinityKey class, which is designed specifically for the purpose of using custom affinity mapping.

public void configureAffinitKeyWithAffinityKeyClass() {
    CacheConfiguration<AffinityKey<Integer>, Person> personCfg = new CacheConfiguration<AffinityKey<Integer>, Person>(
            "persons");
    personCfg.setBackups(1);

    CacheConfiguration<String, Company> companyCfg = new CacheConfiguration<String, Company>("companies");
    companyCfg.setBackups(1);

    Ignite ignite = Ignition.start();

    IgniteCache<AffinityKey<Integer>, Person> personCache = ignite.getOrCreateCache(personCfg);
    IgniteCache<String, Company> companyCache = ignite.getOrCreateCache(companyCfg);

    Company c1 = new Company("company1", "My company");
    Person p1 = new Person(1, c1.getId(), "John");

    // Both the p1 and c1 objects will be cached on the same node
    personCache.put(new AffinityKey<Integer>(p1.getId(), c1.getId()), p1);
    companyCache.put(c1.getId(), c1);

    // get the person object
    p1 = personCache.get(new AffinityKey(1, "company1"));
}