Package com.google.walkaround.util.server.writebatch

Source Code of com.google.walkaround.util.server.writebatch.RandomTest$RequestRunnable$Parameters

/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.walkaround.util.server.writebatch;

import com.google.common.collect.Lists;
import com.google.walkaround.util.server.RetryHelper;
import com.google.walkaround.util.server.RetryHelper.PermanentFailure;
import com.google.walkaround.util.server.RetryHelper.RetryableFailure;
import com.google.walkaround.util.server.writebatch.BatchingUpdateProcessorTest.Result;
import com.google.walkaround.util.server.writebatch.BatchingUpdateProcessorTest.Update;

import junit.framework.TestCase;

import java.security.SecureRandom;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* @author ohler@google.com (Christian Ohler)
*/
public class RandomTest extends TestCase {

  @SuppressWarnings("unused")
  private static final Logger log = Logger.getLogger(RandomTest.class.getName());

  private static boolean randomChance(Random r, double p) {
    return r.nextDouble() < p;
  }

  private static void maybeFail(Random random, double permanentFailureLikelihood,
      double retryableFailureLikelihood)
      throws RetryableFailure, PermanentFailure {
    if (randomChance(random, permanentFailureLikelihood)) {
      throw new PermanentFailure();
    }
    if (randomChance(random, retryableFailureLikelihood)) {
      throw new RetryableFailure();
    }
  }

  private static int randomInt(Random random, int min, int max) {
    return random.nextInt(max - min + 1) + min;
  }

  private static int dummy = 0;

  private static void randomDelay(Random random, int minIterations, int maxIterations) {
    // NOTE(ohler): I originally had a randomized sleep() statements here, but
    // some superficial experiments showed that removing them (and having no
    // delays at all) was _more_ effective at detecting missing locks.
    // Presumably the thread scheduler aligns everything at millisecond (or even
    // 10ms) boundaries, and our critical sections are all much shorter than
    // that.  We now use a busy loop for delays.
    int iterations = randomInt(random, minIterations, maxIterations);
    for (int i = 0; i < iterations; i++) {
      // busy loop
      // dummy++ would cause Eclipse warning "value of the field is not used"
      dummy = dummy + 1;
    }
  }

  private static class Transaction implements UpdateTransaction<Update, Result> {
    static class Parameters {
      double batchTooLargeLikelihood = 0.02;
      double processUpdatePermanentFailureLikelihood = 0.001;
      double processUpdateRetryableFailureLikelihood = 0.01;
      double commitPermanentFailureLikelihood = 0.005;
      double commitRetryableFailureLikelihood = 0.05;
      double updateRejectionLikelihood = 0.1;
      int processUpdateMinDelayIterations = 0;
      int processUpdateMaxDelayIterations = 100;
      int commitMinDelayIterations = 0;
      int commitMaxDelayIterations = 100;
    }

    private final Parameters params;
    private final Random random;
    private int updatesProcessed = 0;

    Transaction(Parameters params, Random random) {
      this.params = params;
      this.random = random;
    }

    @Override
    public Result processUpdate(Update update)
        throws BatchTooLargeException, RetryableFailure, PermanentFailure {
      randomDelay(random,
          params.processUpdateMinDelayIterations, params.processUpdateMaxDelayIterations);
      if (updatesProcessed > 0 && randomChance(random, params.batchTooLargeLikelihood)) {
        throw new BatchTooLargeException("Have already processed " + updatesProcessed);
      }
      maybeFail(random, params.processUpdatePermanentFailureLikelihood,
          params.processUpdateRetryableFailureLikelihood);
      updatesProcessed++;
      return new Result(randomChance(random, params.updateRejectionLikelihood));
    }

    @Override
    public void commit() throws RetryableFailure, PermanentFailure {
      randomDelay(random, params.commitMinDelayIterations, params.commitMaxDelayIterations);
      maybeFail(random, params.commitPermanentFailureLikelihood,
          params.commitRetryableFailureLikelihood);
    }

    @Override
    public void rollback() {
    }
  }

  private static class Factory implements TransactionFactory<Result, Transaction>{
    static class Parameters {
      double beginTransactionPermanentFailureLikelihood = 0.001;
      double beginTransactionRetryableFailureLikelihood = 0.01;
      int beginTransactionMinDelayIterations = 0;
      int beginTransactionMaxDelayIterations = 100;
      Transaction.Parameters transactionParameters = new Transaction.Parameters();
    }

    private final Parameters params;
    private final Random random;
    private int transactionsCreated = 0;

    Factory(Parameters params, Random random) {
      this.params = params;
      this.random = random;
    }

    @Override public Transaction beginTransaction() throws RetryableFailure, PermanentFailure {
      randomDelay(random,
          params.beginTransactionMinDelayIterations, params.beginTransactionMaxDelayIterations);
      maybeFail(random, params.beginTransactionPermanentFailureLikelihood,
          params.beginTransactionRetryableFailureLikelihood);
      transactionsCreated++;
      if (transactionsCreated % 100 == 0) {
        log.info("Transactions created: " + transactionsCreated);
      }
      return new Transaction(params.transactionParameters, random);
    }
  }

  private static class Processor extends BatchingUpdateProcessor<Update, Result, Transaction> {
    public Processor(Factory f) {
      super(f, new RetryHelper());
    }
  }

  private static class RequestRunnable implements Runnable {
    static class Parameters {
      int minRequestsPerThread = 1;
      int maxRequestsPerThread = 100;
    }

    private final Parameters params;
    private final Random random;
    private final Processor processor;

    RequestRunnable(Parameters params, Random random, Processor processor) {
      this.params = params;
      this.random = random;
      this.processor = processor;
    }

    private void sendRequest() {
      try {
        processor.processUpdate(new Update());
      } catch (PermanentFailure f) {
        log.log(Level.INFO, "Permanent failure", f);
      }
    }

    @Override public void run() {
      try {
        int numRequests = randomInt(random,
            params.minRequestsPerThread, params.maxRequestsPerThread);
        for (int i = 0; i < numRequests; i++) {
          sendRequest();
        }
      } catch (Throwable t) {
        log.log(Level.SEVERE, "Uncaught throwable", t);
        t.printStackTrace(System.err);
        System.exit(1);
      }
    }
  }

  private void runTestNoDeadlocksOrAssertionFailures(int numThreads) throws Exception {
    Processor p = new Processor(new Factory(new Factory.Parameters(), new SecureRandom()));
    List<Thread> threads = Lists.newArrayList();
    for (int i = 0; i < numThreads; i++) {
      Thread t = new Thread(
          new RequestRunnable(new RequestRunnable.Parameters(),
              // Every thread has its own Random to avoid the need for
              // synchronization.  We use SecureRandom to make sure the
              // sequences of random numbers are different.
              new SecureRandom(),
              p),
          "Request thread " + i);
      t.start();
      threads.add(t);
    }
    for (Thread t : threads) {
      log.info("Waiting for " + t);
      t.join();
    }
    log.info("Success");
  }

  public void testNoDeadlocksOrAssertionFailures() throws Exception {
    for (int i = 0; i < 100; i++) {
      runTestNoDeadlocksOrAssertionFailures(i % 10 + 1);
    }
  }

  // TODO(ohler): add a test that verifies the actual functionality rather than
  // just "no deadlocks or assertion failures".

}
TOP

Related Classes of com.google.walkaround.util.server.writebatch.RandomTest$RequestRunnable$Parameters

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.