package org.radargun.stages.cache.background; import java.util.List; import java.util.Random; import org.radargun.Operation; import org.radargun.stages.cache.generators.ValueGenerator; import org.radargun.stages.helpers.Range; import org.radargun.stats.Request; import org.radargun.stats.RequestSet; import org.radargun.traits.BasicOperations; import org.radargun.traits.ConditionalOperations; import org.radargun.traits.Transactional; import org.radargun.utils.Utils; /** * Original background stressors logic which loads all entries into cache and then overwrites them. * * @author Radim Vansa <rvansa@redhat.com> * @author Michal Linhard <mlinhard@redhat.com> */ class BackgroundStressorLogic extends AbstractLogic { // these two caches can be transactional internally (with autocommit) // but must not be used between begin() and commit() | rollback() private final BasicOperations.Cache nonTxBasicCache; private final ConditionalOperations.Cache nonTxConditionalCache; private final long keyRangeStart; private final long keyRangeEnd; private final List<Range> deadSlavesRanges; private final boolean loadOnly; private final boolean putWithReplace; private final Random rand = new Random(); private BasicOperations.Cache basicCache; private ConditionalOperations.Cache conditionalCache; private volatile long currentKey; private int remainingTxOps; private boolean loaded; private RequestSet transactionalRequests; private ValueGenerator valueGenerator; BackgroundStressorLogic(BackgroundOpsManager manager, Range range, List<Range> deadSlavesRanges, boolean loaded) { super(manager); this.manager = manager; this.nonTxBasicCache = manager.getBasicCache(); this.nonTxConditionalCache = manager.getConditionalCache(); if (transactionSize <= 0) { basicCache = nonTxBasicCache; conditionalCache = nonTxConditionalCache; } this.keyRangeStart = range.getStart(); this.keyRangeEnd = range.getEnd(); this.deadSlavesRanges = deadSlavesRanges; this.loadOnly = manager.getBackgroundStressorLogicConfiguration().isLoadOnly(); this.putWithReplace = manager.getBackgroundStressorLogicConfiguration().isPutWithReplace(); this.valueGenerator = manager.getBackgroundStressorLogicConfiguration().getValueGenerator(); this.loaded = loaded; this.currentKey = range.getStart(); this.remainingTxOps = transactionSize; } public void loadData() { log.trace("Loading key range [" + keyRangeStart + ", " + keyRangeEnd + "]"); loadKeyRange(keyRangeStart, keyRangeEnd); if (deadSlavesRanges != null) { for (Range range : deadSlavesRanges) { log.trace("Loading key range for dead slave: [" + range.getStart() + ", " + range.getEnd() + "]"); loadKeyRange(range.getStart(), range.getEnd()); } } } private void loadKeyRange(long from, long to) { int loadedKeys = 0; boolean loadWithPutIfAbsent = manager.getBackgroundStressorLogicConfiguration().isLoadWithPutIfAbsent(); int entrySize = manager.getBackgroundStressorLogicConfiguration().getEntrySize(); for (long keyId = from; keyId < to && !stressor.isTerminated(); keyId++, loadedKeys++) { while (!stressor.isTerminated()) { try { Object key = keyGenerator.generateKey(keyId); Object value = valueGenerator.generateValue(keyId, entrySize, rand); if (loadWithPutIfAbsent) { nonTxConditionalCache.putIfAbsent(key, value); } else { nonTxBasicCache.put(key, value); } if (loadedKeys % 1000 == 0) { log.debug("Loaded " + loadedKeys + " out of " + (to - from)); } // if we get an exception, it's OK - we can retry. break; } catch (Exception e) { log.error("Error while loading data", e); } } } log.debug("Loaded all " + (to - from) + " keys"); } @Override public void init() { // TODO: maybe loadData should go here? } public void invoke() throws InterruptedException { if (!loaded) { loadData(); loaded = true; } if (loadOnly) { log.info("Data have been loaded, terminating."); return; } long startTime = 0; Operation operation = getOperation(rand); try { Object key = keyGenerator.generateKey(currentKey++); if (currentKey == keyRangeEnd) { currentKey = keyRangeStart; } if (transactionSize > 0 && remainingTxOps == transactionSize) { ongoingTx = manager.newTransaction(); basicCache = ongoingTx.wrap(nonTxBasicCache); conditionalCache = ongoingTx.wrap(nonTxConditionalCache); Request beginRequest = stressor.stats.startRequest(); beginRequest.exec(Transactional.BEGIN, () -> ongoingTx.begin()); transactionalRequests = stressor.stats.requestSet(); transactionalRequests.add(beginRequest); } Request request = stressor.stats.startRequest(); try { Object result; if (operation == BasicOperations.GET) { result = basicCache.get(key); if (result == null) operation = GET_NULL; } else if (operation == BasicOperations.PUT) { int entrySize = manager.getBackgroundStressorLogicConfiguration().getEntrySize(); Object value = valueGenerator.generateValue(key, entrySize, rand); if (putWithReplace) { conditionalCache.replace(key, value); } else { basicCache.put(key, value); } } else if (operation == BasicOperations.REMOVE) { basicCache.remove(key); } else { throw new IllegalArgumentException(); } request.succeeded(operation); } catch (Exception e) { request.failed(operation); throw e; } finally { if (transactionalRequests != null) { transactionalRequests.add(request); } } if (transactionSize > 0) { remainingTxOps--; if (remainingTxOps == 0) { Request commitRequest = stressor.stats.startRequest(); try { ongoingTx.commit(); commitRequest.succeeded(Transactional.COMMIT); } catch (Exception e) { commitRequest.succeeded(Transactional.COMMIT); throw e; } finally { if (transactionalRequests != null) { transactionalRequests.add(commitRequest); transactionalRequests.finished(commitRequest.isSuccessful(), Transactional.DURATION); } txCleanup(); } remainingTxOps = transactionSize; } } } catch (Exception e) { InterruptedException ie = Utils.findThrowableCauseByClass(e, InterruptedException.class); if (ie != null) { throw ie; } else if (e.getClass().getName().contains("SuspectException")) { log.error("Request failed due to SuspectException: " + e.getMessage()); } else { log.error("Cache operation error", e); } if (transactionSize > 0) { try { ongoingTx.rollback(); } catch (Exception e1) { log.error("Error while ending transaction", e); } finally { txCleanup(); } remainingTxOps = transactionSize; } } } private void txCleanup() { ongoingTx = null; basicCache = null; conditionalCache = null; } @Override public String getStatus() { return String.format("currentKey=%s, remainingTxOps=%d", keyGenerator.generateKey(currentKey), remainingTxOps); } public boolean isLoaded() { return loaded; } }