You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@ignite.apache.org by styriver <Sc...@mgic.com> on 2017/01/25 03:25:35 UTC

Getting previous value in cache immediately after performing an unlock

Below is an example of a test case that simulates the problem we are
experiencing. 

After we put data to a cache and unlock it if we read the value immediately
after unlock
we randomly get the previous value prior to the update. We are utilizing
locks
to prevent data being overwritten between our web application and a possible
back end process
update. I simulated a multi-thread scenario that we would commonly see. We
tried transactions but
the failure rate was even higher. 

package com.xxx.cachemanager.service;

import java.util.concurrent.locks.Lock;

import org.apache.commons.lang.text.StrBuilder;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.Ignition;

public class IgniteCacheLockingExample
{
   private static Object simpleMonitorLock = new Object();
   private PerformSimpleCacheValidationThreadWithLock
simpleCacheValidationWithLock = new
PerformSimpleCacheValidationThreadWithLock();

   public static void main (String[] args)
   {
      IgniteCacheLockingExample locking = new IgniteCacheLockingExample();
      locking.lockTestIgniteExample(true);
   }

   public IgniteCacheLockingExample ()
   {
      simpleCacheValidationWithLock.setDaemon(true);
      simpleCacheValidationWithLock.start();
   }

   public void lockTestIgniteExample (boolean multithread)
   {
      Ignite ignite = null;
      try {
         StrBuilder builder = new StrBuilder("Starting
lockTestIgniteExample(). Multithreading value \"" + multithread + "\"");

         Ignition.setClientMode(true);
         ignite = Ignition.start("testLock.xml");
         System.out.println("Starting lockTestIgniteExample().
Multithreading value \"" + multithread + "\"");

         IgniteCache<Integer, Integer> testLockCache =
ignite.getOrCreateCache("TestLockCache");
         simpleCacheValidationWithLock.setCache(testLockCache);

         // initialize to zero
         testLockCache.removeAll();
         for (int i = 0; i < 20000; i++) {
            testLockCache.put(i, 0);
         }

         int valueCounter = 0;
         for (int i = 0; i < 20000; i++) {
            Integer value = null;
            if (i != 0) {
               if (multithread) {
                  // validate
                  simpleCacheValidationWithLock.setIndex(i - 1);
                 
simpleCacheValidationWithLock.setValueToValidate(valueCounter);
                  synchronized (simpleMonitorLock) {
                     simpleMonitorLock.notifyAll();
                  }
               } else {
                  // validate
                  value = (Integer) testLockCache.get(i - 1);
                  if (value.intValue() != valueCounter) {
                     builder.appendln("Validation after update failed. Cache
Value \"" + value + "\" Test Value \"" + valueCounter + "\"");
                  }
               }
            }

            // lock
            Lock lock = testLockCache.lock(i);
            lock.lock();

            // get
            value = testLockCache.get(i);

            // update and put
            valueCounter++;
            testLockCache.put(i, valueCounter);

            // unlock
            lock.unlock();
         }
         System.out.println(builder.toString());
         System.out.println("Test Complete");
      } catch (Throwable throwable) {
         throwable.printStackTrace();
      } finally {
         Ignition.stop(ignite.name(), false);
      }
   }

   private class PerformSimpleCacheValidationThreadWithLock extends Thread
   {
      private int valueToValidate;
      private int index;
      private IgniteCache<Integer, Integer> testCacheLock;

      public PerformSimpleCacheValidationThreadWithLock ()
      {
      }

      public void run ()
      {
         while (true)
            synchronized (simpleMonitorLock) {
               try {
                  simpleMonitorLock.wait();

                  // We are simulating an independent process updating cache
outside of our
                  // normal web flow. That is why we are locking here. It
will still fail if you
                  // remove the lock below but it fails more frequently with
the lock present.
                  Lock lock = testCacheLock.lock(index);
                  lock.lock();

                  // validate
                  Integer value = (Integer) testCacheLock.get(index);
                  if (value.intValue() != valueToValidate) {
                     System.out.println("Validation after update failed.
Cache Value \"" + value + "\" Test Value \"" + valueToValidate + "\"");
                  }

                  lock.unlock();
               } catch (InterruptedException e) {
               }
            }
      }

      public void setValueToValidate (int valueToValidate)
      {
         this.valueToValidate = valueToValidate;
      }

      public void setIndex (int index)
      {
         this.index = index;
      }

      public void setCache (IgniteCache<Integer, Integer> testCacheLock)
      {
         this.testCacheLock = testCacheLock;
      }
   }
}


Here is our client configuration

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd">

	    <bean id="ignite.cfg"
class="org.apache.ignite.configuration.IgniteConfiguration">    
     
		<property name="gridLogger"> 
			<bean class="org.apache.ignite.logger.slf4j.Slf4jLogger" />
		</property>
		
		<property name="peerClassLoadingEnabled" value="false"/>
		   	
    	<property name="cacheConfiguration">
			<list>       		
		        <bean parent="testLockCache">
        		</bean>        		
			</list>
		</property>
		
	        
        <property name="discoverySpi">
            <bean
class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
                <property name="ipFinder">
                    
                    
                    
                    <bean
class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
                        <property name="addresses">
                            <list>
                                
                                <value>127.0.0.1:47500..47509</value>
                            </list>
                        </property>
                    </bean>
                </property>
            </bean>
        </property>      	
	</bean>    
	
	
	<bean id="testLockCache"
class="org.apache.ignite.configuration.CacheConfiguration">           
	    
	  <property name="name" value="TestLockCache"/>
	  
	    
	  <property name="cacheMode" value="REPLICATED"/>
	
	    
	  <property name="memoryMode" value="ONHEAP_TIERED"/>
	
	    
	  <property name="swapEnabled" value="false"/>  
	  
	    
	  <property name="atomicityMode" value="TRANSACTIONAL" />
	  
	  <property name="writeSynchronizationMode" value="FULL_SYNC"/>
  
	</bean>   		
	
</beans>



--
View this message in context: http://apache-ignite-users.70518.x6.nabble.com/Getting-previous-value-in-cache-immediately-after-performing-an-unlock-tp10236.html
Sent from the Apache Ignite Users mailing list archive at Nabble.com.

Re: Getting previous value in cache immediately after performing an unlock

Posted by vkulichenko <va...@gmail.com>.
Hi,

Your test is incorrect. This lines are not synchronized and can be reordered
with each other and with the validation that happens in another thread:

simpleCacheValidationWithLock.setIndex(i - 1);
simpleCacheValidationWithLock.setValueToValidate(valueCounter);

After I moved them into the synchronized block, test always passes.

BTW, for such transactional updates it's better to use
PESSIMISTIC/REPEATABLE_READ transactions:
https://apacheignite.readme.io/docs/transactions

-Val



--
View this message in context: http://apache-ignite-users.70518.x6.nabble.com/Getting-previous-value-in-cache-immediately-after-performing-an-unlock-tp10236p10254.html
Sent from the Apache Ignite Users mailing list archive at Nabble.com.