Package org.radargun.stages.cache.background

Source Code of org.radargun.stages.cache.background.SharedLogLogic

package org.radargun.stages.cache.background;

import java.util.Map;

import org.radargun.Operation;
import org.radargun.traits.BasicOperations;
import org.radargun.traits.ConditionalOperations;

/**
* This logic operates on {@link SharedLogValue shared log values}
* and requires {@link ConditionalOperations} on the cache.
* With this setup, multiple stressors can change one log value concurrently.
*
* @author Radim Vansa <rvansa@redhat.com>
*/
class SharedLogLogic extends AbstractLogLogic<SharedLogValue> {

   private final ConditionalOperations.Cache nonTxConditionalCache;
   private ConditionalOperations.Cache conditionalCache;
   private final long numEntries;

   SharedLogLogic(BackgroundOpsManager manager, long threadId, long numEntries) {
      super(manager, threadId);
      nonTxConditionalCache = manager.getConditionalCache();
      if (transactionSize <= 0) {
         conditionalCache = nonTxConditionalCache;
      }
      this.numEntries = numEntries;
   }

   @Override
   protected long nextKeyId() {
      return keySelectorRandom.nextLong() % numEntries;
   }

   @Override
   protected boolean invokeLogic(long keyId) throws Exception {
      Operation operation = manager.getOperation(operationTypeRandom);

      // In shared mode, we can't ever atomically modify the two keys (main and backup) to have only
      // one of them with the actual value (this is not true even for private mode but there the moment
      // when we move the value from main to backup or vice versa does not cause any problem, because the
      // worst thing is to read slightly outdated value). However, here the invariant holds that the operation
      // must be recorded in at least one of the entries, but the situation with both of these having
      // some value is valid (although, we try to evade it be conditionally removing one of them in each
      // logic step).
      SharedLogValue prevValue, backupValue, nextValue;
      do {
         prevValue = checkedGetValue(keyId);
         backupValue = checkedGetValue(~keyId);
         nextValue = getNextValue(prevValue, backupValue);
         if (stressor.isTerminated() || stressor.isInterrupted()) return false;
      } while (nextValue == null);
      // now for modify operations, execute it
      if (operation == BasicOperations.PUT) {
         if (checkedPutValue(keyId, prevValue, nextValue)) {
            if (backupValue != null) {
               delayedRemoveValue(~keyId, backupValue);
            }
         } else {
            return false;
         }
      } else if (operation == BasicOperations.REMOVE) {
         if (checkedPutValue(~keyId, backupValue, nextValue)) {
            if (prevValue != null) {
               delayedRemoveValue(keyId, prevValue);
            }
         } else {
            return false;
         }
      } else {
         // especially GETs are not allowed here, because these would break the deterministic order
         // - each operationId must be written somewhere
         throw new UnsupportedOperationException("Only PUT and REMOVE operations are allowed for this logic.");
      }
      return true;
   }

   private SharedLogValue getNextValue(SharedLogValue prevValue, SharedLogValue backupValue) throws StressorException, BreakTxRequest {
      if (prevValue == null && backupValue == null) {
         return new SharedLogValue(stressor.id, operationId);
      } else if (prevValue != null && backupValue != null) {
         SharedLogValue joinValue = prevValue.join(backupValue);
         if (joinValue.size() >= manager.getLogLogicConfiguration().getValueMaxSize()) {
            return filterAndAddOperation(joinValue);
         } else {
            return joinValue.with(stressor.id, operationId);
         }
      }
      SharedLogValue value = prevValue != null ? prevValue : backupValue;
      if (value.size() < manager.getLogLogicConfiguration().getValueMaxSize()) {
         return value.with(stressor.id, operationId);
      } else {
         return filterAndAddOperation(value);
      }
   }

   private SharedLogValue filterAndAddOperation(SharedLogValue value) throws StressorException, BreakTxRequest {
      Map<Integer, Long> operationIds = getCheckedOperations(value.minFrom(stressor.id));
      SharedLogValue filtered = value.with(stressor.id, operationId, operationIds);
      if (filtered.size() > manager.getLogLogicConfiguration().getValueMaxSize()) {
         return null;
      } else {
         return filtered;
      }
   }

   @Override
   protected void startTransaction() {
      ongoingTx = manager.newTransaction();
      basicCache = ongoingTx.wrap(nonTxBasicCache);
      conditionalCache = ongoingTx.wrap(nonTxConditionalCache);
      ongoingTx.begin();
   }

   @Override
   protected void clearTransaction() {
      super.clearTransaction();
      conditionalCache = null;
   }

   private SharedLogValue checkedGetValue(long keyId) throws Exception {
      Object prevValue;
      long startTime = System.nanoTime();
      try {
         prevValue = basicCache.get(keyGenerator.generateKey(keyId));
      } catch (Exception e) {
         stressor.stats.registerError(System.nanoTime() - startTime, BasicOperations.GET);
         throw e;
      }
      long endTime = System.nanoTime();
      if (prevValue != null && !(prevValue instanceof SharedLogValue)) {
         stressor.stats.registerError(endTime - startTime, BasicOperations.GET);
         log.error("Value is not an instance of SharedLogValue: " + prevValue);
         throw new IllegalStateException();
      } else {
         stressor.stats.registerRequest(endTime - startTime, prevValue == null ? GET_NULL : BasicOperations.GET);
         return (SharedLogValue) prevValue;
      }
   }

   private boolean checkedPutValue(long keyId, SharedLogValue oldValue, SharedLogValue newValue) throws Exception {
      boolean returnValue;
      long startTime = System.nanoTime();
      Operation operation = Operation.UNKNOWN;
      try {
         if (oldValue == null) {
            operation = ConditionalOperations.PUT_IF_ABSENT;
            returnValue = conditionalCache.putIfAbsent(keyGenerator.generateKey(keyId), newValue);
         } else {
            operation = ConditionalOperations.REPLACE;
            returnValue = conditionalCache.replace(keyGenerator.generateKey(keyId), oldValue, newValue);
         }
      } catch (Exception e) {
         stressor.stats.registerError(System.nanoTime() - startTime, operation);
         throw e;
      }
      long endTime = System.nanoTime();
      stressor.stats.registerRequest(endTime - startTime, operation);
      return returnValue;
   }

   @Override
   protected boolean checkedRemoveValue(long keyId, SharedLogValue oldValue) throws Exception {
      long startTime = System.nanoTime();
      try {
         boolean returnValue = conditionalCache.remove(keyGenerator.generateKey(keyId), oldValue);
         long endTime = System.nanoTime();
         stressor.stats.registerRequest(endTime - startTime, ConditionalOperations.REMOVE);
         return returnValue;
      } catch (Exception e) {
         stressor.stats.registerError(System.nanoTime() - startTime, ConditionalOperations.REMOVE);
         throw e;
      }
   }
}
TOP

Related Classes of org.radargun.stages.cache.background.SharedLogLogic

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.