0

I'm trying to synchronize access to an object stored in Ignite using transactions, and finding that the results of one transaction often overwrite the results of another. I've written up a simpler version of what I'm trying to do as a JUnit test for ease of testing:

import junit.framework.TestCase;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.transactions.Transaction;
import org.apache.ignite.transactions.TransactionConcurrency;
import org.apache.ignite.transactions.TransactionIsolation;

public class IgniteTransactionTest extends TestCase {
    private Ignite ignite;

    @Override
    protected void setUp() throws Exception {
        // Start up a non-client Ignite for tests
        IgniteConfiguration config = new IgniteConfiguration();
        ignite = Ignition.getOrStart(config);
    }

    public void testTransaction() throws InterruptedException {
        IgniteCache cache = ignite.getOrCreateCache("cache");
        cache.put("counter", 0);

        Runnable r = () -> {
            for (int i = 0; i < 1000; i++) {
                Transaction tx = ignite.transactions().txStart(
                        TransactionConcurrency.PESSIMISTIC, TransactionIsolation.SERIALIZABLE);

                int counter = (int) cache.get("counter");
                counter += 1;
                cache.put("counter", counter);

                try {
                    tx.commit();
                } catch (Exception ex) {
                    System.out.println("Commit failed");
                    i--;
                }
            }
        };

        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        assertEquals((int) cache.get("counter"), 2000);
    }

    @Override
    protected void tearDown() throws Exception {
        ignite.close();
    }
}

Basically, it runs two separate threads trying to increment a counter stored in an Ignite cache, with isolation level SERIALIZABLE, and then checks that the counter has the correct value. According to the documentation for Ignite transactions:

TransactionIsolation.SERIALIZABLE isolation level means that all transactions occur in a completely isolated fashion, as if all transactions in the system had executed serially, one after the other. Read access with this level happens the same way as with TransactionIsolation.REPEATABLE_READ level. However, in TransactionConcurrency.OPTIMISTIC mode, if some transactions cannot be serially isolated from each other, then one winner will be picked and the other transactions in conflict will result in TransactionOptimisticException being thrown.

But running this test produces

junit.framework.AssertionFailedError: expected:<1613> but was:<2000>

indicating that a write in one thread can be interleaved between a read and a write in the other thread. Additionally, the documentation suggests that a failed transaction should throw an exception when attempting to commit, but this never occurs.

Am I miss understanding how to use transactions in the first place? How do I keep them isolated?

F. Dall
  • 28
  • 4

1 Answers1

3

You create cache without CacheConfiguration provided. By default, Atomic cache was created and no transactional features are supported with this cache.

You need smth like this:

ignite.getOrCreateCache(new CacheConfiguration<>("cache")
    .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL));