Search code examples
javaignitefault-tolerance

Ignite - full sync configuration


I have two server ignite nodes (Each node is started in the Spring Boot application) in the cluster.

And i have two cache:

//Persistence cahce

configuration.setReadThrough(true);
    configuration.setWriteThrough(true);
    configuration.setCacheStoreFactory(storeFactory);
    configuration.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
    configuration.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
    configuration.setCacheMode(CacheMode.REPLICATED);

//in memory

configuration.setIndexedTypes(String.class, SequenceReserve.class);
    configuration.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
    configuration.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
    configuration.setCacheMode(CacheMode.REPLICATED);

Requests for update to any caches can go to each node in parallel.

Every update - atomic operation.

cache.invoke(...);

My main goal is to avoid a inconsistent data any cost. In memory cache can get lost, but should not be inconsistent.

Any node should return an exception if the transaction was not commit on all nodes.

Can I write such a configuration that this behavior is guaranteed with 100% probability.

UPDATED

I ran the test and got the following behavior:

Each request is always performed on the same node (invoke method). I believe this is correct behavior. When will the query be executed on the second node?


Solution

  • IgniteCache#invoke(...) is a transactional operation. The best way to learn it is to check whether it throws TransactionException.

    Your configuration seems to be enough to guarantee data consistency between nodes.

    If you mean consistency between these two caches, then you can start explicit transactions and run invoke-s within them.

    UPD

    Note, that, as mentioned in JavaDoc for invoke(..) method, your EntryProcessor should be stateless. It may be called multiple times on different nodes, so it should return the same value each time.

    UPD 2

    If you call IgniteCache#invoke() method on a transactional cache, it makes the provided EntryProcessor be called on every node, that contains the needed partition of this cache. But if the cache is atomic, then the EntryProcessor will be called on the primary node only.

    But you shouldn't rely on this behaviour. It's not specified anywhere, so it may change in future versions. Ignite is free to make as many calls to EntryProcessor#process() as it's necessary to guarantee data consistency.

    You can use the following code to verify my words:

    public static void main(String[] args) throws IgniteException {
        Ignite ignite = Ignition.start("examples/config/example-ignite.xml");
    
        IgniteCache<Integer, String> atomicCache = ignite.getOrCreateCache(
            cacheConfiguration("atomic", CacheAtomicityMode.ATOMIC));
    
        IgniteCache<Integer, String> txCache = ignite.getOrCreateCache(
            cacheConfiguration("transactional", CacheAtomicityMode.TRANSACTIONAL));
    
        atomicCache.invoke(1, (entry, arguments) -> {
            System.out.println("Atomic invoke");
            return null;
        });
    
        txCache.invoke(1, (entry, arguments) -> {
            System.out.println("Transactional invoke");
            return null;
        });
    }
    
    private static <K, V> CacheConfiguration<K, V> cacheConfiguration(String name, CacheAtomicityMode atomicity) {
        CacheConfiguration<K, V> cacheCfg = new CacheConfiguration<>(name);
        cacheCfg.setAtomicityMode(atomicity);
        cacheCfg.setCacheMode(CacheMode.REPLICATED);
        cacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
    
        return cacheCfg;
    }
    

    "Transactional invoke" will be printed on every node, but "Atomic invoke" –– only on a single one.