Added caching support for org/env information
diff --git a/common_helper.go b/common_helper.go
index e67855a..65aee3d 100644
--- a/common_helper.go
+++ b/common_helper.go
@@ -27,6 +27,13 @@
 // read while its being written to and vice versa
 var tenantCachelock = sync.RWMutex{}
 
+// Cache for all org/env for this cluster
+var orgEnvCache map[string]bool
+
+// RW lock for orgEnvCache map cache since the cache can be
+// read while its being written to and vice versa
+var orgEnvCacheLock = sync.RWMutex{}
+
 // Cache for apiKey~tenantId to developer related information
 var developerInfoCache map[string]developerInfo
 
@@ -65,6 +72,32 @@
 
 // Load data scope information into an in-memory cache so that
 // for each record a DB lookup is not required
+func createOrgEnvCache(snapshot *common.Snapshot) {
+	// Lock before writing to the map as it has multiple readers
+	orgEnvCacheLock.Lock()
+	defer orgEnvCacheLock.Unlock()
+	orgEnvCache = make(map[string]bool)
+
+	for _, table := range snapshot.Tables {
+		switch table.Name {
+		case "edgex.data_scope":
+			for _, row := range table.Rows {
+				var org, env string
+
+				row.Get("org", &org)
+				row.Get("env", &env)
+				orgEnv := getKeyForOrgEnvCache(org, env)
+				if orgEnv != "" {
+					orgEnvCache[orgEnv] = true
+				}
+			}
+		}
+	}
+	log.Debugf("Count of org~env in the cache: %d", len(orgEnvCache))
+}
+
+// Load data scope information into an in-memory cache so that
+// for each record a DB lookup is not required
 func updateDeveloperInfoCache() {
 	// Lock before writing to the map as it has multiple readers
 	developerInfoCacheLock.Lock()
@@ -178,6 +211,37 @@
 tenant_id in combination with apiKey is used to find kms related information
 */
 func validateTenant(tenant *tenant) (bool, dbError) {
+	if config.GetBool(useCaching) {
+		// acquire a read lock as this cache has 1 writer as well
+		orgEnvCacheLock.RLock()
+		orgEnv := getKeyForOrgEnvCache(tenant.Org, tenant.Env)
+		_, exists := orgEnvCache[orgEnv]
+		orgEnvCacheLock.RUnlock()
+		dbErr := dbError{}
+		if !exists {
+			log.Debugf("OrgEnv = %s not found "+
+				"in cache", orgEnv)
+			log.Debug("loading info from DB")
+
+			// Update cache
+			valid, dbErr := validateTenantFromDB(tenant)
+			if valid {
+				// update cache
+				orgEnvCacheLock.Lock()
+				defer orgEnvCacheLock.Unlock()
+				orgEnvCache[orgEnv] = true
+			}
+			return valid, dbErr
+		} else {
+			return true, dbErr
+		}
+	} else {
+		return validateTenantFromDB(tenant)
+	}
+
+}
+
+func validateTenantFromDB(tenant *tenant) (bool, dbError) {
 	db := getDB()
 	error := db.QueryRow("SELECT scope FROM edgex_data_scope"+
 		" where org = ? and env = ?", &tenant.Org, &tenant.Env).Scan(&tenant.TenantId)
@@ -249,3 +313,7 @@
 func getKeyForDeveloperInfoCache(tenantId string, apiKey string) string {
 	return tenantId + "~" + apiKey
 }
+
+func getKeyForOrgEnvCache(org, env string) string {
+	return org + "~" + env
+}
diff --git a/common_helper_test.go b/common_helper_test.go
index 972f4b3..42010ed 100644
--- a/common_helper_test.go
+++ b/common_helper_test.go
@@ -157,10 +157,59 @@
 })
 
 var _ = Describe("test validateTenant()", func() {
+	Context("with usecaching set to true", func() {
+		BeforeEach(func() {
+			config.Set(useCaching, true)
+			snapshot := getDatascopeSnapshot()
+			createOrgEnvCache(&snapshot)
+			Expect(len(orgEnvCache)).To(Equal(1))
+		})
+		AfterEach(func() {
+			config.Set(useCaching, false)
+		})
+		Context("valididate existing org/env", func() {
+			It("should return true", func() {
+				tenant := tenant{Org: "testorg", Env: "testenv"}
+				valid, dbError := validateTenant(&tenant)
+				Expect(dbError.Reason).To(Equal(""))
+				Expect(valid).To(BeTrue())
+			})
+		})
+
+		Context("get tenant for invalid scopeuuid", func() {
+			It("should return false", func() {
+				tenant := tenant{Org: "wrongorg", Env: "wrongenv"}
+				valid, dbError := validateTenant(&tenant)
+				Expect(dbError.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
+				Expect(valid).To(BeFalse())
+			})
+		})
+	})
+	Context("with usecaching set to false", func() {
+		Context("valididate existing org/env", func() {
+			It("should return true", func() {
+				tenant := tenant{Org: "testorg", Env: "testenv"}
+				valid, dbError := validateTenant(&tenant)
+				Expect(dbError.Reason).To(Equal(""))
+				Expect(valid).To(BeTrue())
+			})
+		})
+		Context("get tenant for invalid scopeuuid", func() {
+			It("should return false", func() {
+				tenant := tenant{Org: "wrongorg", Env: "wrongenv"}
+				valid, dbError := validateTenant(&tenant)
+				Expect(dbError.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
+				Expect(valid).To(BeFalse())
+			})
+		})
+	})
+})
+
+var _ = Describe("test validateTenantFromDB()", func() {
 	Context("validate tenant for org/env that exists in DB", func() {
 		It("should not return an error and tenantId should be populated", func() {
 			tenant := tenant{Org: "testorg", Env: "testenv"}
-			valid, dbError := validateTenant(&tenant)
+			valid, dbError := validateTenantFromDB(&tenant)
 			Expect(valid).To(BeTrue())
 			Expect(tenant.TenantId).To(Equal("tenantid"))
 			Expect(dbError.ErrorCode).To(Equal(""))
@@ -169,7 +218,7 @@
 	Context("validate tenant for org/env that do not exist in DB", func() {
 		It("should return error with unknown_scope", func() {
 			tenant := tenant{Org: "wrongorg", Env: "wrongenv"}
-			valid, dbError := validateTenant(&tenant)
+			valid, dbError := validateTenantFromDB(&tenant)
 			Expect(valid).To(BeFalse())
 			Expect(dbError.ErrorCode).To(Equal("UNKNOWN_SCOPE"))
 		})
@@ -194,6 +243,20 @@
 	})
 })
 
+var _ = Describe("test getKeyForDeveloperInfoCache()", func() {
+	It("should return key using tenantid and apiKey", func() {
+		res := getKeyForDeveloperInfoCache("testid", "testapikey")
+		Expect(res).To(Equal("testid~testapikey"))
+	})
+})
+
+var _ = Describe("test getKeyForOrgEnvCache()", func() {
+	It("should return key using org and env", func() {
+		res := getKeyForDeveloperInfoCache("testorg", "testenv")
+		Expect(res).To(Equal("testorg~testenv"))
+	})
+})
+
 func getDatascopeSnapshot() common.Snapshot {
 	event := common.Snapshot{
 		SnapshotInfo: "test_snapshot_valid",
diff --git a/listener.go b/listener.go
index d0c3a42..4dfe9fa 100644
--- a/listener.go
+++ b/listener.go
@@ -54,6 +54,8 @@
 		createTenantCache(snapshot)
 		log.Debug("Created a local cache" +
 			" for datasope information")
+		createOrgEnvCache(snapshot)
+		log.Debug("Created a local cache for org~env Information")
 	} else {
 		log.Info("Will not be caching any developer or tenant info " +
 			"and make a DB call for every analytics msg")
@@ -77,6 +79,10 @@
 					// map as it has multiple readers
 					tenantCachelock.Lock()
 					defer tenantCachelock.Unlock()
+
+					orgEnvCacheLock.Lock()
+					defer orgEnvCacheLock.Unlock()
+
 					for _, ele := range rows {
 						var scopeuuid, tenantid string
 						var org, env string
@@ -93,6 +99,14 @@
 								"tenantCache. Added "+
 								"scope: "+"%s", scopeuuid)
 						}
+
+						orgEnv := getKeyForOrgEnvCache(org, env)
+						if orgEnv != "" {
+							orgEnvCache[orgEnv] = true
+							log.Debugf("Refreshed local "+
+								"orgEnvCache. Added "+
+								"orgEnv: "+"%s", orgEnv)
+						}
 					}
 				case common.Delete:
 					rows = append(rows, payload.OldRow)
@@ -100,15 +114,27 @@
 					// as it has multiple readers
 					tenantCachelock.Lock()
 					defer tenantCachelock.Unlock()
+
+					orgEnvCacheLock.Lock()
+					defer orgEnvCacheLock.Unlock()
 					for _, ele := range rows {
-						var scopeuuid string
+						var scopeuuid, org, env string
 						ele.Get("id", &scopeuuid)
+						ele.Get("org", &org)
+						ele.Get("env", &env)
 						if scopeuuid != "" {
 							delete(tenantCache, scopeuuid)
 							log.Debugf("Refreshed local"+
 								" tenantCache. Deleted"+
 								" scope: %s", scopeuuid)
 						}
+						orgEnv := getKeyForOrgEnvCache(org, env)
+						if orgEnv != "" {
+							delete(orgEnvCache, orgEnv)
+							log.Debugf("Refreshed local"+
+								" orgEnvCache. Deleted"+
+								" org~env: %s", orgEnv)
+						}
 					}
 				}
 			}
diff --git a/listener_test.go b/listener_test.go
index 1de46ba..6c9e094 100644
--- a/listener_test.go
+++ b/listener_test.go
@@ -38,7 +38,9 @@
 
 		snapshot := getDatascopeSnapshot()
 		createTenantCache(&snapshot)
+		createOrgEnvCache(&snapshot)
 		Expect(len(tenantCache)).To(Equal(1))
+		Expect(len(orgEnvCache)).To(Equal(1))
 	})
 
 	AfterEach(func() {
@@ -92,6 +94,9 @@
 			Expect(tenant.TenantId).To(Equal("s"))
 			Expect(tenant.Org).To(Equal("o"))
 			Expect(tenant.Env).To(Equal("e"))
+
+			orgEnv := getKeyForOrgEnvCache("o", "e")
+			Expect(orgEnvCache[orgEnv]).To(BeTrue())
 		})
 	})
 
@@ -140,6 +145,9 @@
 				Expect(tenant.Org).To(Equal("o2"))
 				Expect(tenant.Env).To(Equal("e2"))
 
+				orgEnv := getKeyForOrgEnvCache("o2", "e2")
+				Expect(orgEnvCache[orgEnv]).To(BeTrue())
+
 				txn, err = getDB().Begin()
 				Expect(err).ShouldNot(HaveOccurred())
 				txn.Exec("DELETE FROM edgex_data_scope where id = 'i2'")
@@ -159,6 +167,9 @@
 				handler.Handle(&delete)
 				_, exists := tenantCache["i2"]
 				Expect(exists).To(BeFalse())
+
+				_, exists = orgEnvCache[orgEnv]
+				Expect(exists).To(BeFalse())
 			})
 		})
 	})