Package org.radargun.stages.cache

Source Code of org.radargun.stages.cache.IsolationLevelCheckStage$ReaderThread

package org.radargun.stages.cache;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.radargun.DistStageAck;
import org.radargun.StageResult;
import org.radargun.config.Property;
import org.radargun.config.Stage;
import org.radargun.state.SlaveState;
import org.radargun.traits.BasicOperations;
import org.radargun.traits.InjectTrait;
import org.radargun.traits.Transactional;
import org.radargun.utils.Projections;
import org.radargun.utils.TimeConverter;

/**
* Stage for testing guaranties of isolation levels.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
@Stage(doc = "Stage for testing guaranties of isolation levels.")
public class IsolationLevelCheckStage extends CheckStage {

   private static final String ISOLATION_CHECK_KEY = "isolationCheckKey";
   public static final String REPEATABLE_READ = "REPEATABLE_READ";
   public static final String READ_COMMITTED = "READ_COMMITTED";

   @Property(doc = "Number of concurrent threads that modify the value. Default is 2.")
   private int writers = 2;

   @Property(doc = "Number of concurrent threads that try to retrieve the value. Default is 10.")
   private int readers = 10;

   @Property(converter = TimeConverter.class, doc = "How long should this stage take. Default is 1 minute.")
   private long duration = 60000;

   @Property(doc = "Number of reads executed inside on transaction. Default is 30.")
   private int transactionSize = 30;

   @Property(optional = false, doc = "Expected isolation level (should match to cache configuration). Supported " +
         "values are [" + IsolationLevelCheckStage.READ_COMMITTED + ", " + IsolationLevelCheckStage.REPEATABLE_READ + "].")
   private String expectedLevel;

   private volatile boolean finished;
   private volatile boolean valueChangeDetected = false;

   @InjectTrait
   private BasicOperations basicOperations;

   @InjectTrait
   private Transactional transactional;

   @Override
   public DistStageAck executeOnSlave() {
      if (expectedLevel == null) {
         return errorResponse("No expected level set", null);
      } else if (!expectedLevel.equalsIgnoreCase(REPEATABLE_READ) && !expectedLevel.equalsIgnoreCase(READ_COMMITTED)) {
         return errorResponse("Expected level should be one of " + READ_COMMITTED + " and " + REPEATABLE_READ, null);
      }
      BasicOperations.Cache<Object, Object> cache = basicOperations.getCache(null);
      try {
         cache.put(ISOLATION_CHECK_KEY, new Long(0));
      } catch (Exception e) {
         return errorResponse("Failed to insert first value", e);
      }
      List<ClientThread> threads = new ArrayList<ClientThread>();
      for (int i = 0; i < writers; ++i) {
         ClientThread t = new WriterThread();
         t.setName("Writer-" + i);
         t.start();
         threads.add(t);
      }
      for (int i = 0; i < readers; ++i) {
         ClientThread t = new ReaderThread();
         t.setName("Reader-" + i);
         t.start();
         threads.add(t);
      }
      try {
         Thread.sleep(duration);
      } catch (InterruptedException e) {
      }
      finished = true;
      DistStageAck error = checkThreads(threads);
      if (error != null) return error;
      return new ChangeAck(slaveState, valueChangeDetected);
   }

   @Override
   public StageResult processAckOnMaster(List<DistStageAck> acks) {
      StageResult result = super.processAckOnMaster(acks);
      if (result.isError()) return result;

      boolean anyValueChangeDetected = false;
      for (ChangeAck ack : Projections.instancesOf(acks, ChangeAck.class)) {
         log.debugf("Value change detected on slave %d: %s", ack.getSlaveIndex(), ack.valueChangeDetected);
         if (expectedLevel.equalsIgnoreCase(REPEATABLE_READ) && ack.valueChangeDetected) {
            log.error("Value change was detected but this should not happen with isolation " + expectedLevel);
            return errorResult();
         }
         anyValueChangeDetected |= ack.valueChangeDetected;
      }
      if (expectedLevel.equalsIgnoreCase(READ_COMMITTED) && !anyValueChangeDetected) {
         log.error("Value change was expected with isolation " + expectedLevel + " but none was detected");
         return errorResult();
      }
      return StageResult.SUCCESS;
   }

   private static class ChangeAck extends DistStageAck {
      final boolean valueChangeDetected;

      private ChangeAck(SlaveState slaveState, boolean valueChangeDetected) {
         super(slaveState);
         this.valueChangeDetected = valueChangeDetected;
      }
   }

   private class WriterThread extends ClientThread {
      @Override
      public void run() {
         BasicOperations.Cache nonTxCache = basicOperations.getCache(null);
         Random rand = new Random();
         while (!finished) {
            try {
               Transactional.Transaction tx = transactional.getTransaction();
               BasicOperations.Cache txCache = tx.wrap(nonTxCache);
               log.trace("Starting transaction");
               tx.begin();
               txCache.put(ISOLATION_CHECK_KEY, -1);
               Thread.sleep(10);
               long value = rand.nextInt(1000);
               txCache.put(ISOLATION_CHECK_KEY, value);
               log.trace("Inserted value " + value);
               tx.commit();
               log.trace("Ended transaction");
            } catch (Exception e) {
               exception = e;
               return;
            }
         }
      }
   }

   private class ReaderThread extends ClientThread {
      @Override
      public void run() {
         BasicOperations.Cache nonTxCache = basicOperations.getCache(null);
         while (!finished) {
            Transactional.Transaction tx = transactional.getTransaction();
            BasicOperations.Cache txCache = tx.wrap(nonTxCache);
            log.trace("Starting transaction");
            tx.begin();
            Object lastValue = null;
            for (int i = 0; i < transactionSize; ++i) {
               try {
                  Object value = txCache.get(ISOLATION_CHECK_KEY);
                  log.trace("Read value " + value + ", previous value is " + lastValue);
                  if (!(value instanceof Long) || ((Long) value) < 0) {
                     exception = new IllegalStateException("Unexpected value " + value);
                     tx.rollback();
                     return;
                  }
                  if (lastValue != null && !lastValue.equals(value)) {
                     log.trace("Change detected");
                     valueChangeDetected = true;
                  }
                  lastValue = value;
                  Thread.sleep(10);
               } catch (Exception e) {
                  exception = e;
                  return;
               }
            }
            tx.commit();
            log.trace("Ended transaction");
         }
      }
   }
}
TOP

Related Classes of org.radargun.stages.cache.IsolationLevelCheckStage$ReaderThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.