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()) }) }) })