You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by to...@apache.org on 2015/08/03 22:31:02 UTC

[18/21] incubator-usergrid git commit: USERGRID-871: add audit simulations

USERGRID-871: add audit simulations


Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/32fefb98
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/32fefb98
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/32fefb98

Branch: refs/heads/two-dot-o-dev
Commit: 32fefb98cf5c0a5885e5453a1a3183deaa65c708
Parents: 3976ff9
Author: Mike Dunker <md...@apigee.com>
Authored: Fri Jul 31 22:24:33 2015 -0700
Committer: Mike Dunker <md...@apigee.com>
Committed: Fri Jul 31 22:24:33 2015 -0700

----------------------------------------------------------------------
 .../loadtests/runAuditGetCollectionEntities.sh  |  71 ++++++++++
 .../runAuditVerifyCollectionEntities.sh         |  71 ++++++++++
 .../datagenerators/FeederGenerator.scala        |  60 ++++++---
 .../usergrid/enums/ConfigProperties.scala       |   8 +-
 .../apache/usergrid/enums/ScenarioType.scala    |   4 +-
 .../apache/usergrid/helpers/Extractors.scala    |   7 +
 .../org/apache/usergrid/helpers/Setup.scala     |  27 ++++
 .../usergrid/scenarios/AuditScenarios.scala     | 133 +++++++++++++++++++
 .../scenarios/EntityCollectionScenarios.scala   |   5 +-
 .../org/apache/usergrid/settings/Settings.scala |  97 +++++++++++++-
 .../usergrid/simulations/AuditSimulation.scala  |  68 ++++++++++
 stack/loadtests/src/test/resources/dummy.csv    |   1 -
 .../src/test/resources/dummyAuditUuid.csv       |   1 +
 .../loadtests/src/test/resources/dummyUuid.csv  |   1 +
 14 files changed, 526 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/runAuditGetCollectionEntities.sh
----------------------------------------------------------------------
diff --git a/stack/loadtests/runAuditGetCollectionEntities.sh b/stack/loadtests/runAuditGetCollectionEntities.sh
new file mode 100755
index 0000000..f34ea54
--- /dev/null
+++ b/stack/loadtests/runAuditGetCollectionEntities.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# 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.
+#
+
+die() { echo "$@" 1>&2 ; exit 1; }
+
+####
+#This is a script to simplify running gatling tests.  It will default several parameters, invoke the maven plugins
+#Then aggregate the results
+####
+[ "$#" -ge 4 ] || die "At least 4 arguments required, $# provided.  Example is $0 URL RAMP_USERS RAMP_TIME(seconds) AUDIT_UUID_FILENAME"
+
+URL="$1"
+RAMP_USERS="$2"
+RAMP_TIME="$3"
+AUDIT_UUID_FILENAME="$4"
+
+shift 4
+
+#Compile everything
+mvn compile
+
+#Set the app id to be a date epoch for uniqueness
+#APP=$(date +%s)
+ADMIN_USER=superuser
+ADMIN_PASSWORD=test
+ORG=gatling
+APP=millionentities
+SCENARIO_TYPE=auditGetCollectionEntities
+SEARCH_LIMIT=1000
+
+AUTH_TYPE=token
+TOKEN_TYPE=management
+
+#Execute the test
+mvn gatling:execute \
+-Dorg=${ORG} \
+-Dapp=${APP} \
+-Dbaseurl=${URL} \
+-DadminUser=${ADMIN_USER}  \
+-DadminPassword=${ADMIN_PASSWORD}  \
+-DrampUsers=${RAMP_USERS}  \
+-DrampTime=${RAMP_TIME}  \
+-DscenarioType=${SCENARIO_TYPE} \
+-DauthType=${AUTH_TYPE} \
+-DtokenType=${TOKEN_TYPE} \
+-DauditUuidFilename=${AUDIT_UUID_FILENAME} \
+-DsearchLimit=${SEARCH_LIMIT} \
+-Dgatling.simulationClass=org.apache.usergrid.simulations.AuditSimulation
+
+
+
+#Now move all the reports
+#AGGREGATE_DIR="target/aggregate-$(date +%s)"
+
+#mkdir -p ${AGGREGATE_DIR}
+
+#copy to the format of target/aggregate(date)/(simnulationame)-simulation.log
+#find target -name "simulation.log" -exec cp {} ${AGGREGATE_DIR}/$(basename $(dirname {} ))-simulation.log  \;
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/runAuditVerifyCollectionEntities.sh
----------------------------------------------------------------------
diff --git a/stack/loadtests/runAuditVerifyCollectionEntities.sh b/stack/loadtests/runAuditVerifyCollectionEntities.sh
new file mode 100755
index 0000000..f0af5de
--- /dev/null
+++ b/stack/loadtests/runAuditVerifyCollectionEntities.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+#
+# 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.
+#
+
+die() { echo "$@" 1>&2 ; exit 1; }
+
+####
+#This is a script to simplify running gatling tests.  It will default several parameters, invoke the maven plugins
+#Then aggregate the results
+####
+[ "$#" -ge 4 ] || die "At least 4 arguments required, $# provided.  Example is $0 URL RAMP_USERS RAMP_TIME(seconds) AUDIT_UUID_FILENAME [FAILED_UUID_FILENAME]"
+
+URL="$1"
+RAMP_USERS="$2"
+RAMP_TIME="$3"
+AUDIT_UUID_FILENAME="$4"
+FAILED_UUID_FILENAME="$5"
+
+shift 5
+
+#Compile everything
+mvn compile
+
+#Set the app id to be a date epoch for uniqueness
+#APP=$(date +%s)
+ADMIN_USER=superuser
+ADMIN_PASSWORD=test
+ORG=gatling
+APP=millionentities
+SCENARIO_TYPE=auditVerifyCollectionEntities
+
+AUTH_TYPE=token
+TOKEN_TYPE=management
+
+#Execute the test
+mvn gatling:execute \
+-Dorg=${ORG} \
+-Dapp=${APP} \
+-Dbaseurl=${URL} \
+-DadminUser=${ADMIN_USER}  \
+-DadminPassword=${ADMIN_PASSWORD}  \
+-DrampUsers=${RAMP_USERS}  \
+-DrampTime=${RAMP_TIME}  \
+-DscenarioType=${SCENARIO_TYPE} \
+-DauthType=${AUTH_TYPE} \
+-DtokenType=${TOKEN_TYPE} \
+-DauditUuidFilename=${AUDIT_UUID_FILENAME} \
+-DfailedUuidFilename=${FAILED_UUID_FILENAME} \
+-Dgatling.simulationClass=org.apache.usergrid.simulations.AuditSimulation
+
+
+
+#Now move all the reports
+#AGGREGATE_DIR="target/aggregate-$(date +%s)"
+
+#mkdir -p ${AGGREGATE_DIR}
+
+#copy to the format of target/aggregate(date)/(simnulationame)-simulation.log
+#find target -name "simulation.log" -exec cp {} ${AGGREGATE_DIR}/$(basename $(dirname {} ))-simulation.log  \;
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/datagenerators/FeederGenerator.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/datagenerators/FeederGenerator.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/datagenerators/FeederGenerator.scala
index 09030f5..61641de 100755
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/datagenerators/FeederGenerator.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/datagenerators/FeederGenerator.scala
@@ -19,9 +19,10 @@ package org.apache.usergrid.datagenerators
 import java.util.UUID
 import java.util.concurrent.atomic.AtomicInteger
 import io.gatling.core.Predef._
-import org.apache.usergrid.helpers.Utils
+import org.apache.usergrid.helpers.{Setup, Utils}
 import org.apache.usergrid.settings.Settings
 import scala.collection.mutable.ArrayBuffer
+import scala.io.Source
 
 object FeederGenerator {
 
@@ -231,26 +232,51 @@ object FeederGenerator {
    }
  }
 
-   /*
- def generateCustomEntityFeeder(numEntities: Int, entityType: String, prefix: String, seed: Int = 1): Feeder[String] =
- new Feeder[String] {
-   var counter = new AtomicInteger(seed)
+  def collectionNameFeeder: Feeder[String] = new Feeder[String] {
+    val list: List[String] = Setup.getCollectionsList
+    var counter = new AtomicInteger(0)
 
-   // runs forever -- users detect when data is done using validEntity field
-   override def hasNext: Boolean = true
+    override def hasNext: Boolean = true
 
-   override def next(): Map[String, String] = {
-     val i = counter.getAndIncrement()
-     val entityName = prefix.concat(i.toString)
-     val entity = EntityDataGenerator.generateEntity(entityType, entityName)
-     val entityUrl = Settings.baseCollectionUrl + "/" + entityName
-     val validEntity = if (i >= seed + numEntities) "no" else "yes"
+    override def next(): Map[String, String] = {
+      val i = counter.getAndIncrement()
+      val collectionName = if (i < list.length) list(i) else ""
+      val validEntity = if (i >= list.length) "no" else "yes"
 
-     Map("entityName" -> entityName, "entity" -> entity, "entityUrl" -> entityUrl, "validEntity" -> validEntity)
-   }
- }
+      Map("collectionName" -> collectionName, "validEntity" -> validEntity)
+    }
+  }
 
-    */
+  def collectionCsvFeeder: Feeder[String] = new Feeder[String] {
+    val csvLines = if (Settings.feedAuditUuids) Source.fromFile(Settings.feedAuditUuidFilename).getLines().toArray else Array[String]()
+    val csvLinesLen = csvLines.length
+    var counter = new AtomicInteger(0)
+
+    override def hasNext: Boolean = true
+
+    def getNextLine: String = {
+      do {
+        val i = counter.getAndIncrement()
+        if (i >= csvLinesLen) return null
+
+        val line = csvLines(i)
+        if (line != Settings.auditUuidsHeader) return line
+
+      } while (true)
+
+      null
+    }
+
+    override def next: Map[String, String] = {
+      val line = getNextLine
+      val validEntity = if (line == null) "no" else "yes"
+      val array = if (line != null) line.split(",") else null
+      val collectionName = if (line != null) array(0) else ""
+      val uuid = if (line != null) array(1) else ""
+
+      Map("collectionName" -> collectionName, "uuid" -> uuid, "validEntity" -> validEntity)
+    }
+  }
 
  def generateCustomEntityInfiniteFeeder(seed: Int = Settings.entitySeed, entityType: String = Settings.entityType, prefix: String = Settings.entityPrefix): Iterator[String] = {
    Iterator.from(seed).map(i=>EntityDataGenerator.generateEntity(entityType, prefix.concat(i.toString)))

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ConfigProperties.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ConfigProperties.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ConfigProperties.scala
index 7e6cf55..75228a7 100644
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ConfigProperties.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ConfigProperties.scala
@@ -51,6 +51,8 @@ object ConfigProperties {
   val EntityWorkerCount = "entityWorkerCount"
   val EntityWorkerNum = "entityWorkerNum"
   val UuidFilename = "uuidFilename"
+  val AuditUuidFilename = "auditUuidFilename"
+  val FailedUuidFilename = "failedUuidFilename"
   val SandboxCollection = "sandboxCollection"
   val PurgeUsers = "purgeUsers"
 
@@ -59,7 +61,7 @@ object ConfigProperties {
     NumDevices,Collection,RampTime,Throttle,RpsTarget,RpsRampTime,HoldDuration,PushNotifier,PushProvider,EntityPrefix,
     EntityType,EntitySeed,SearchLimit,SearchQuery,EndConditionType,EndMinutes,EndRequestCount,OrgCreationUsername,
     OrgCreationName,OrgCreationEmail,OrgCreationPassword,UpdateProperty,UpdateValue,EntityWorkerCount,EntityWorkerNum,
-    UuidFilename,SandboxCollection,PurgeUsers)
+    UuidFilename,AuditUuidFilename,FailedUuidFilename,SandboxCollection,PurgeUsers)
 
   def isValid(str: String): Boolean = {
     Values.contains(str)
@@ -113,7 +115,9 @@ object ConfigProperties {
         case UpdateValue => new Date().toString
         case EntityWorkerCount => 0
         case EntityWorkerNum => 0
-        case UuidFilename => "dummy.csv"
+        case UuidFilename => "dummyUuid.csv"
+        case AuditUuidFilename => "dummyAuditUuid.csv"
+        case FailedUuidFilename => "/tmp/dummy.csv"
         case SandboxCollection => false
         case PurgeUsers => 100
       }

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ScenarioType.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ScenarioType.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ScenarioType.scala
index 760c837..76fae7b 100644
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ScenarioType.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/enums/ScenarioType.scala
@@ -8,9 +8,11 @@ object ScenarioType {
   val UpdateEntities = "updateEntities"
   val UuidRandomInfinite = "uuidRandomInfinite"
   val GetByNameSequential = "getByNameSequential"
+  val AuditGetCollectionEntities = "auditGetCollectionEntities"
+  val AuditVerifyCollectionEntities = "auditVerifyCollectionEntities"
 
   val Values = Seq(GetAllByCursor,NameRandomInfinite,LoadEntities,DeleteEntities,UpdateEntities,UuidRandomInfinite,
-    GetByNameSequential)
+    GetByNameSequential,AuditGetCollectionEntities,AuditVerifyCollectionEntities)
 
   def isValid(str: String): Boolean = {
     Values.contains(str)

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Extractors.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Extractors.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Extractors.scala
index e970fbd..221c0a7 100644
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Extractors.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Extractors.scala
@@ -51,6 +51,13 @@ object Extractors {
   }
 
   /**
+   * Will extract the uuids from the get collection response.
+   */
+  def extractCollectionUuids(saveAsName: String) = {
+    jsonPath("$.entities[*]").ofType[Map[String,Any]].findAll.transformOption(extract => { extract.orElse(Some(Seq.empty)) }).saveAs(saveAsName)
+  }
+
+  /**
    * tries to extract the cursor from the session, if it exists, it returns true. if it's the default, returns false
    * @param nameInSession The name of the variable in the session
    * @return

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Setup.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Setup.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Setup.scala
index 727c295..37428ac 100644
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Setup.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/helpers/Setup.scala
@@ -22,11 +22,13 @@ package org.apache.usergrid.helpers
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import com.ning.http.client.{ListenableFuture, AsyncHttpClient,Response}
+import io.gatling.jsonpath.JsonPath
 import org.apache.usergrid.datagenerators.FeederGenerator
 import org.apache.usergrid.enums.TokenType
 import org.apache.usergrid.settings.Settings
 
 import scala.collection.mutable.ArrayBuffer
+import scala.util.parsing.json.JSON
 
 object Setup {
   var managementToken:String = null
@@ -109,6 +111,31 @@ object Setup {
     statusCode
   }
 
+  def getCollectionsList: List[String] = {
+    val appInfoGet = client
+      .prepareGet(s"${Settings.baseAppUrl}")
+      .setHeader("Cache-Control", "no-cache")
+      .setHeader("Accept", "application/json; charset=UTF-8")
+      .setHeader("Authorization", s"Bearer $getManagementToken")
+      .build()
+
+    val getResponse = client.executeRequest(appInfoGet).get()
+    val topLevel: Map[String, Any] = JSON.parseFull(getResponse.getResponseBody).get.asInstanceOf[Map[String,Any]]
+    val entities: List[Map[String, Any]] = topLevel("entities").asInstanceOf[List[Map[String,Any]]]
+    //println(s"entities: $entities")
+    val firstEntity: Map[String, Any] = entities.head
+    //println(s"firstEntity: $firstEntity")
+    val metadata: Map[String, Any] = firstEntity("metadata").asInstanceOf[Map[String, Any]]
+    //println(s"metadata: $metadata")
+    val collections: Map[String, Any] = metadata("collections").asInstanceOf[Map[String, Any]]
+    //println(s"collections: $collections")
+
+    val collectionsList: List[String] = (collections map { case (key, value) => key }).toList
+    //println(collectionsList)
+
+    collectionsList
+  }
+
   def sandboxCollection(): Integer = {
 
     val sandboxCollectionPost = client

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/AuditScenarios.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/AuditScenarios.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/AuditScenarios.scala
new file mode 100644
index 0000000..b58ee07
--- /dev/null
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/AuditScenarios.scala
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.usergrid.scenarios
+
+import io.gatling.core.Predef._
+import io.gatling.core.feeder.RecordSeqFeederBuilder
+import io.gatling.http.Predef._
+import org.apache.usergrid.datagenerators.FeederGenerator
+import org.apache.usergrid.enums.{AuthType, EndConditionType}
+import org.apache.usergrid.helpers.Extractors._
+import org.apache.usergrid.helpers.Utils
+import org.apache.usergrid.settings.{Headers, Settings}
+
+/**
+ * Provides CRUD methods for audits
+ */
+object AuditScenarios {
+
+  //The value for the cursor
+  val SessionVarCursor: String = "cursor"
+  val SessionVarUuid: String = "entityUuid"
+  val SessionVarCollectionName: String = "collectionName"
+  val SessionVarCollectionUuids: String = "collectionUuids"
+
+  def collectionGetUrl(useCursor: Boolean): String = {
+    val url = "/${" + SessionVarCollectionName + "}?" +
+      (if (useCursor) "cursor=${" + SessionVarCursor + "}&" else "") +
+      (if (Settings.searchQuery != "") s"ql=${Settings.searchQuery}&" else "") +
+      (if (Settings.searchLimit > 0) s"limit=${Settings.searchLimit}&" else "")
+
+    // remove trailing & or ?
+    url.dropRight(1)
+  }
+
+  val getCollectionsWithoutCursor = exec(
+    http("GET collections")
+      .get(collectionGetUrl(false))
+      .headers(Headers.authToken)
+      .check(status.is(200), extractCollectionUuids(SessionVarCollectionUuids), maybeExtractCursor(SessionVarCursor)))
+      .foreach("${" + SessionVarCollectionUuids + "}", "singleUuid") {
+        exec(session => {
+          val uuidObj = session("singleUuid").as[Map[String,Any]]
+          val uuid = uuidObj("uuid").asInstanceOf[String]
+          val collectionName = session(SessionVarCollectionName).as[String]
+          Settings.addAuditUuid(uuid, collectionName)
+          session
+        })
+      }
+
+  val getCollectionsWithCursor = exec(
+    http("GET collections")
+      .get(collectionGetUrl(true))
+      .headers(Headers.authToken)
+      .check(status.is(200), extractCollectionUuids(SessionVarCollectionUuids), maybeExtractCursor(SessionVarCursor)))
+      .foreach("${" + SessionVarCollectionUuids + "}", "singleUuid") {
+        exec(session => {
+          val uuidObj = session("singleUuid").as[Map[String,Any]]
+          val uuid = uuidObj("uuid").asInstanceOf[String]
+          val collectionName = session(SessionVarCollectionName).as[String]
+          Settings.addAuditUuid(uuid, collectionName)
+          session
+        })
+      }
+
+  val getAllCollections = scenario("Get all collections")
+    .exec(injectTokenIntoSession())
+    .exec(injectAuthType())
+    .asLongAs(session => session("validEntity").asOption[String].map(validEntity => validEntity != "no").getOrElse[Boolean](true)) {
+      feed(FeederGenerator.collectionNameFeeder)
+        .exec{
+          session => if (session("validEntity").as[String] == "yes") { println("Getting collection " + session("collectionName").as[String]) }
+          session
+        }
+        .doIf(session => session("validEntity").as[String] == "yes") {
+          exec(getCollectionsWithoutCursor)
+            .asLongAs(stringParamExists(SessionVarCursor)) {
+              exec(getCollectionsWithCursor)
+          }
+        }
+    }
+
+  val getCollectionEntity = exec(
+    http("GET collection entity")
+      .get("/${collectionName}?ql=uuid=${uuid}")
+      .headers(Headers.authToken)
+      .check(status.is(200), jsonPath("$.count").saveAs("count")))
+        .exec(session => {
+          val count = session("count").as[String].toInt
+          val uuid = session("uuid").as[String]
+          val collectionName = session(SessionVarCollectionName).as[String]
+
+          // save items not found
+          if (count < 1) {
+            Settings.addAuditUuid(uuid, collectionName)
+            Settings.incAuditNotFound()
+            println(s"NOT FOUND: $collectionName.$uuid")
+          } else if (count > 1) {
+            Settings.addAuditUuid(uuid, collectionName)
+            Settings.incAuditBadResponse()
+            println(s"INVALID RESPONSE (count=$count): $collectionName.$uuid")
+          } else {
+            // println(s"FOUND: $collectionName.$uuid")
+            Settings.incAuditSuccess()
+          }
+
+          session
+        })
+
+  val verifyCollections = scenario("Verify collections")
+    .exec(injectTokenIntoSession())
+    .exec(injectAuthType())
+    .asLongAs(session => session("validEntity").asOption[String].map(validEntity => validEntity != "no").getOrElse[Boolean](true)) {
+    feed(FeederGenerator.collectionCsvFeeder)
+      .doIf(session => session("validEntity").as[String] == "yes") {
+        exec(getCollectionEntity)
+      }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/EntityCollectionScenarios.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/EntityCollectionScenarios.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/EntityCollectionScenarios.scala
index 5f679ec..a760931 100644
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/EntityCollectionScenarios.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/scenarios/EntityCollectionScenarios.scala
@@ -48,16 +48,17 @@ object EntityCollectionScenarios {
   }
 
   def entityGetByNameUrl(entityName: String): String = {
-    val url = s"/${Settings.collection}/$entityName"
 
-    url
+    s"/${Settings.collection}/$entityName"
   }
 
   def randomEntityNameUrl(prefix: String = Settings.entityPrefix, numEntities: Int = Settings.numEntities, seed: Int = Settings.entitySeed): String = {
+
     Utils.randomEntityNameUrl(prefix, numEntities, seed, Settings.baseCollectionUrl)
   }
 
   def uuidFeeder(): RecordSeqFeederBuilder[String] = {
+
     csv(Settings.feedUuidFilename).random
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/settings/Settings.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/settings/Settings.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/settings/Settings.scala
index e4be5c8..3c1c2e2 100755
--- a/stack/loadtests/src/main/scala/org/apache/usergrid/settings/Settings.scala
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/settings/Settings.scala
@@ -17,6 +17,7 @@
 package org.apache.usergrid.settings
 
 import java.io.{PrintWriter, FileOutputStream}
+import java.util.concurrent.atomic.AtomicInteger
 import javax.xml.bind.DatatypeConverter
 import io.gatling.http.Predef._
 import io.gatling.http.config.HttpProtocolBuilder
@@ -149,12 +150,32 @@ object Settings {
   val numEntities:Int = entitiesPerWorkerFloor + extraEntity
 
   // UUID log file, have to go through this because creating a csv feeder with an invalid csv file fails at maven compile time
-  private val dummyCsv = ConfigProperties.getDefault(ConfigProperties.UuidFilename).toString
+  private val dummyTestCsv = ConfigProperties.getDefault(ConfigProperties.UuidFilename).toString
+  private val dummyAuditCsv = ConfigProperties.getDefault(ConfigProperties.AuditUuidFilename).toString
+  private val dummyAuditFailedCsv = ConfigProperties.getDefault(ConfigProperties.FailedUuidFilename).toString
+
   private val uuidFilename = initStrSetting(ConfigProperties.UuidFilename)
-  val captureUuids = uuidFilename != dummyCsv && (scenarioType == ScenarioType.LoadEntities || scenarioType == ScenarioType.GetByNameSequential)
-  val feedUuids = uuidFilename != dummyCsv && scenarioType == ScenarioType.UuidRandomInfinite
-  val captureUuidFilename = if (captureUuids) uuidFilename else dummyCsv
-  val feedUuidFilename = if (feedUuids) uuidFilename else dummyCsv
+  private val auditUuidFilename = initStrSetting(ConfigProperties.AuditUuidFilename)
+  private val failedUuidFilename = initStrSetting(ConfigProperties.FailedUuidFilename)
+
+  // feeds require valid files, even if test won't be run
+  val feedUuids = uuidFilename != dummyTestCsv && scenarioType == ScenarioType.UuidRandomInfinite
+  val feedUuidFilename = uuidFilename
+
+  val feedAuditUuids = uuidFilename != dummyAuditCsv && scenarioType == ScenarioType.AuditVerifyCollectionEntities
+  val feedAuditUuidFilename = auditUuidFilename
+
+  // also uses uuidFilename
+  val captureUuids = uuidFilename != dummyTestCsv && (scenarioType == ScenarioType.LoadEntities || scenarioType == ScenarioType.GetByNameSequential)
+  val captureUuidFilename = uuidFilename
+
+  val captureAuditUuids = (auditUuidFilename != dummyAuditCsv && scenarioType == ScenarioType.AuditGetCollectionEntities) ||
+                          (failedUuidFilename != dummyAuditFailedCsv && scenarioType == ScenarioType.AuditVerifyCollectionEntities)
+  println(s"auditUuidFilename=$auditUuidFilename")
+  println(s"captureAuditUuids=$captureAuditUuids")
+  val captureAuditUuidFilename = if (scenarioType == ScenarioType.AuditVerifyCollectionEntities) failedUuidFilename else auditUuidFilename
+  println(s"captureAuditUuidFilename=$captureAuditUuidFilename")
+
   val purgeUsers:Int = initIntSetting(ConfigProperties.PurgeUsers)
 
   private var uuidMap: Map[Int, String] = Map()
@@ -179,6 +200,32 @@ object Settings {
     }
   }
 
+
+  val auditUuidsHeader = "collection,uuid"
+
+  // key: uuid, value: collection
+  private var auditUuidMap: Map[String,String] = Map()
+  def addAuditUuid(uuid: String, collection: String): Unit = {
+    if (captureAuditUuids) auditUuidMap += (uuid -> collection)
+  }
+
+  def writeAuditUuidsToFile(uuidDesc: String): Unit = {
+    if (captureAuditUuids) {
+      println(s"Sorting and writing ${auditUuidMap.size} ${uuidDesc} UUIDs in CSV file ${captureAuditUuidFilename}")
+      val writer = {
+        val fos = new FileOutputStream(captureAuditUuidFilename)
+        new PrintWriter(fos, false)
+      }
+      writer.println(auditUuidsHeader)
+      val uuidList: List[(String, String)] = auditUuidMap.toList.sortBy(l => (l._2, l._1))
+      uuidList.foreach { l =>
+        writer.println(s"${l._2},${l._1}")
+      }
+      writer.flush()
+      writer.close()
+    }
+  }
+
   def getUserFeeder:Array[Map[String, String]]= {
     FeederGenerator.generateUserWithGeolocationFeeder(totalUsers, userLocationRadius, centerLatitude, centerLongitude)
   }
@@ -201,9 +248,48 @@ object Settings {
     (System.currentTimeMillis() - testStartTime) < (endMinutes.toLong*60L*1000L)
   }
 
+  private val countAuditSuccess = new AtomicInteger(0)
+  private val countAuditNotFound = new AtomicInteger(0)
+  private val countAuditBadResponse = new AtomicInteger(0)
+
+  def incAuditSuccess(): Unit = {
+    countAuditSuccess.incrementAndGet()
+  }
+
+  def incAuditNotFound(): Unit = {
+    countAuditNotFound.incrementAndGet()
+  }
+
+  def incAuditBadResponse(): Unit = {
+    countAuditBadResponse.incrementAndGet()
+  }
+
+  def printAuditResults(): Unit = {
+    if (scenarioType == ScenarioType.AuditVerifyCollectionEntities) {
+      val countSuccess = countAuditSuccess.get
+      val countNotFound = countAuditNotFound.get
+      val countBadResponse = countAuditBadResponse.get
+      val countTotal = countSuccess + countNotFound + countBadResponse
+
+      println()
+      println("-----------------------------------------------------------------------------")
+      println("AUDIT RESULTS")
+      println("-----------------------------------------------------------------------------")
+      println()
+      println(s"Successful:   ${countSuccess}")
+      println(s"Not Found:    ${countNotFound}")
+      println(s"Bad Response: ${countBadResponse}")
+      println(s"Total:        ${countTotal}")
+      println()
+      println("-----------------------------------------------------------------------------")
+      println()
+    }
+  }
+
   def printSettingsSummary(): Unit = {
     val authTypeStr = authType + (if (authType == AuthType.Token) s"($tokenType)" else "")
     val endConditionStr = if (endConditionType == EndConditionType.MinutesElapsed) s"$endMinutes minutes elapsed" else s"$endRequestCount requests"
+    println()
     println("-----------------------------------------------------------------------------")
     println("SIMULATION SETTINGS")
     println("-----------------------------------------------------------------------------")
@@ -222,6 +308,7 @@ object Settings {
     println(s"End Condition:$endConditionStr")
     println()
     println("-----------------------------------------------------------------------------")
+    println()
   }
 
   printSettingsSummary()

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/main/scala/org/apache/usergrid/simulations/AuditSimulation.scala
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/main/scala/org/apache/usergrid/simulations/AuditSimulation.scala b/stack/loadtests/src/main/scala/org/apache/usergrid/simulations/AuditSimulation.scala
new file mode 100755
index 0000000..48b5f60
--- /dev/null
+++ b/stack/loadtests/src/main/scala/org/apache/usergrid/simulations/AuditSimulation.scala
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.usergrid.simulations
+
+import io.gatling.core.Predef._
+import io.gatling.core.structure.ScenarioBuilder
+import org.apache.usergrid.enums.ScenarioType
+import org.apache.usergrid.helpers.Setup
+import org.apache.usergrid.scenarios.{AuditScenarios, EntityCollectionScenarios}
+import org.apache.usergrid.settings.Settings
+
+/**
+ * Audit simulations.
+ *
+ */
+class AuditSimulation extends Simulation {
+
+  def getScenario(scenarioType: String): ScenarioBuilder = {
+    scenarioType match {
+      case ScenarioType.AuditGetCollectionEntities => AuditScenarios.getAllCollections
+      case ScenarioType.AuditVerifyCollectionEntities => AuditScenarios.verifyCollections
+    }
+  }
+
+  before{
+  }
+
+  Settings.setTestStartTime()
+  if (ScenarioType.isValid(Settings.scenarioType)) {
+    val scenario: ScenarioBuilder = getScenario(Settings.scenarioType)
+    setUp(
+      scenario
+        .inject(
+          rampUsers(Settings.rampUsers) over Settings.rampTime
+        ).protocols(Settings.httpConf.acceptHeader("application/json"))
+    )
+  } else {
+    println(s"Audit scenario type ${Settings.scenarioType} not found.")
+  }
+
+  after {
+    if (Settings.captureAuditUuids) {
+      val uuidDesc = Settings.scenarioType match {
+        case ScenarioType.AuditGetCollectionEntities => "found"
+        case ScenarioType.AuditVerifyCollectionEntities => "failed"
+      }
+      Settings.writeAuditUuidsToFile(uuidDesc)
+    }
+    Settings.printSettingsSummary()
+    Settings.printAuditResults()
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/test/resources/dummy.csv
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/test/resources/dummy.csv b/stack/loadtests/src/test/resources/dummy.csv
deleted file mode 100644
index 187f36b..0000000
--- a/stack/loadtests/src/test/resources/dummy.csv
+++ /dev/null
@@ -1 +0,0 @@
-name,uuid

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/test/resources/dummyAuditUuid.csv
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/test/resources/dummyAuditUuid.csv b/stack/loadtests/src/test/resources/dummyAuditUuid.csv
new file mode 100644
index 0000000..fc9f2fb
--- /dev/null
+++ b/stack/loadtests/src/test/resources/dummyAuditUuid.csv
@@ -0,0 +1 @@
+collection,uuid

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/32fefb98/stack/loadtests/src/test/resources/dummyUuid.csv
----------------------------------------------------------------------
diff --git a/stack/loadtests/src/test/resources/dummyUuid.csv b/stack/loadtests/src/test/resources/dummyUuid.csv
new file mode 100644
index 0000000..187f36b
--- /dev/null
+++ b/stack/loadtests/src/test/resources/dummyUuid.csv
@@ -0,0 +1 @@
+name,uuid