public void Set(DatastoreEntities context, RepositorySchema schema, DataQuery query, int repositoryId, Guid id, DataQueryResults results) { if (!ConfigHelper.AllowCaching) { return; } if (results == null) { return; } //Do not cache big items if (results.RecordList.Count > 100) { return; } if (!string.IsNullOrEmpty(query.Keyword) && !this.FTSReadyCache.IsReady(id)) { return; } //if (!string.IsNullOrEmpty(query.Keyword) && !ConfigHelper.AllowCacheWithKeyword) return; var timer = Stopwatch.StartNew(); var cache = RepositoryCacheManager.GetCache(id, RepositoryManager.GetSchemaParentId(repositoryId)); long lockTime = 0; var changeStamp = 0; var queryHash = 0; var subCacheKey = GetSubKey(schema, query); try { //Some queries should be cached a long time var longCache = !query.FieldFilters.Any() && !query.FieldSorts.Any() && string.IsNullOrEmpty(query.Keyword) && !query.SkipDimensions.Any(); var extraMinutes = longCache ? 480 : 0; var coreHash = 0; CacheResultsQuery item; using (var q = new AcquireReaderLock(ServerUtilities.RandomizeGuid(cache.ID, RSeed), "QueryCache")) { lockTime += q.LockTime; queryHash = query.GetHashCode(); if (!query.ExcludeCount && query.IncludeDimensions && !query.IncludeEmptyDimensions) { coreHash = query.CoreHashCode(); } changeStamp = RepositoryManager.GetRepositoryChangeStamp(context, repositoryId); lock (cache) { item = cache?.FirstOrDefault(x => x.QueryHash == queryHash && x.ChangeStamp == changeStamp); } //If data has not changed and results are in cache then do nothing except mark as accessed if (item != null) { item.Results = results; item.Timestamp = DateTime.Now.AddMinutes(extraMinutes); item.SubKey = subCacheKey; return; } } lock (cache) { using (var q = new AcquireWriterLock(ServerUtilities.RandomizeGuid(cache.ID, RSeed), "QueryCache")) { lockTime += q.LockTime; //Create a new cache item item = new CacheResultsQuery() { QueryHash = queryHash, QueryCoreHash = coreHash, RepositoryId = repositoryId, ChangeStamp = changeStamp, Results = results, QueryString = query.ToString(), ParentId = RepositoryManager.GetSchemaParentId(repositoryId), Timestamp = DateTime.Now.AddMinutes(extraMinutes), SubKey = subCacheKey, }; cache.Add(item); } } } catch (Exception ex) { timer.Stop(); LoggerCQ.LogError(ex, $"RepositoryId={id}, Elapsed={timer.ElapsedMilliseconds}, ID={id}, LockTime={lockTime}, Count={cache.Count}, QueryHash={queryHash}, ChangeStamp={changeStamp}"); throw; } finally { timer.Stop(); if (timer.ElapsedMilliseconds > 50) { LoggerCQ.LogWarning($"Slow cache set: Elapsed={timer.ElapsedMilliseconds}, LockTime={lockTime}, Count={cache.Count}, ID={id}, Query=\"{query.ToString()}\""); } LoggerCQ.LogTrace($"QueryCache: Set: SubCacheKey={subCacheKey}"); } }
public DataQueryResults Get(DatastoreEntities context, RepositorySchema schema, DataQuery query, int repositoryId, Guid id, out bool isCore) { isCore = false; if (!ConfigHelper.AllowCaching) { return(null); } long lockTime = 0; int queryHash = 0; int coreHash = 0; int changeStamp = 0; var task1 = Task.Factory.StartNew(() => { queryHash = query.GetHashCode(); coreHash = query.CoreHashCode(); changeStamp = RepositoryManager.GetRepositoryChangeStamp(context, repositoryId); }); var timer = new Stopwatch(); var cache = RepositoryCacheManager.GetCache(id, RepositoryManager.GetSchemaParentId(repositoryId)); try { using (var q = new AcquireReaderLock(ServerUtilities.RandomizeGuid(cache.ID, RSeed), "QueryCache")) { lockTime = q.LockTime; timer.Start(); //Ensure that the pre-calculations are complete task1.Wait(); CacheResultsQuery item = null; lock (cache) { item = cache?.FirstOrDefault(x => x.QueryHash == queryHash && x.ChangeStamp == changeStamp); } if (item == null) //return null; { if (ConfigHelper.AllowCoreCache) { //TODO: OPTIMIZE: this is a linear search of thousands of items!!!! //If did not find a match then find see if core properties match //If so we can use the dimension and count values and just replace the records collection lock (cache) { item = cache?.FirstOrDefault(x => x.QueryCoreHash == coreHash && x.ChangeStamp == changeStamp); } } if (item == null) { return(null); } isCore = true; item.HitCount++; return(item.Results); } item.Timestamp = DateTime.Now; item.HitCount++; return(item.Results); } } catch (Exception ex) { timer.Stop(); LoggerCQ.LogError(ex, $"RepositoryId={id}, Elapsed={timer.ElapsedMilliseconds}, LockTime={lockTime}, Count={cache.Count}, QueryHash={queryHash}, ChangeStamp={changeStamp}, ID={id}"); throw; } finally { timer.Stop(); if (timer.ElapsedMilliseconds > 50) { LoggerCQ.LogWarning($"Slow cache get: Elapsed={timer.ElapsedMilliseconds}, LockTime={lockTime}, Count={cache.Count}, ID={id}, QueryString=\"{query.ToString()}\""); } } }