ITRexSpatialMemoryCacheContext[] CacheContexts) GetIntermediary(ICombinedFilter filter) { // Construct the appropriate list of surveyed surfaces // Obtain local reference to surveyed surface list. If it is replaced while processing the // list then the local reference will still be valid allowing lock free read access to the list. ISurveyedSurfaces filteredSurveyedSurfaces = null; var surveyedSurfaceList = siteModel.SurveyedSurfaces; if (includeSurveyedSurfaceInformation && surveyedSurfaceList?.Count > 0) { filteredSurveyedSurfaces = DIContext.Obtain <ISurveyedSurfaces>(); // Filter out any surveyed surfaces which don't match current filter (if any) - realistically, this is time filters we're thinking of here surveyedSurfaceList.FilterSurveyedSurfaceDetails(filter.AttributeFilter.HasTimeFilter, filter.AttributeFilter.StartTime, filter.AttributeFilter.EndTime, filter.AttributeFilter.ExcludeSurveyedSurfaces(), filteredSurveyedSurfaces, filter.AttributeFilter.SurveyedSurfaceExclusionList); // Ensure that the filtered surveyed surfaces are in a known ordered state filteredSurveyedSurfaces.SortChronologically(filter.AttributeFilter.ReturnEarliestFilteredCellPass); } var filteredSurveyedSurfacesAsArray = filteredSurveyedSurfaces?.Count > 0 ? filteredSurveyedSurfaces.Select(s => s.ID).ToArray() : new Guid[0]; // Get a caching context for the sub grids returned by this requester, but only if the requested grid data type supports it ITRexSpatialMemoryCacheContext[] subGridCacheContexts = null; if (_enableGeneralSubGridResultCaching && ClientLeafSubGrid.SupportsAssignationFromCachedPreProcessedClientSubGrid[(int)gridDataType]) { filter.AttributeFilter.SiteModel = siteModel; var contextType1 = Utilities.IntermediaryICGridDataTypeForDataType(gridDataType, true); var contextType2 = Utilities.IntermediaryICGridDataTypeForDataType(gridDataType, false); var context1 = SubGridCache?.LocateOrCreateContext(siteModel.ID, contextType1, SpatialCacheFingerprint.ConstructFingerprint(siteModel.ID, contextType1, filter, filteredSurveyedSurfacesAsArray)); var context2 = SubGridCache?.LocateOrCreateContext(siteModel.ID, contextType2, SpatialCacheFingerprint.ConstructFingerprint(siteModel.ID, contextType2, filter, filteredSurveyedSurfacesAsArray)); subGridCacheContexts = new[] { context1, context2 }.Where(x => x != null).ToArray(); } return(gridDataType, filter, filteredSurveyedSurfaces, _surfaceElevationPatchRequestFactory(SubGridCache, SubGridCache?.LocateOrCreateContext(siteModel.ID, GridDataType.SurveyedSurfaceHeightAndTime, SpatialCacheFingerprint.ConstructFingerprint(siteModel.ID, GridDataType.SurveyedSurfaceHeightAndTime, null, filteredSurveyedSurfaces?.Select(x => x.ID).ToArray() ?? new Guid[0]))), subGridCacheContexts); }
public void Test_TRexSpatialMemoryCacheTests_DesignChangeInvalidation() { var projectUid = Guid.NewGuid(); var gridDataType = Types.GridDataType.PassCount; var designUid = Guid.NewGuid(); var designUid2 = Guid.NewGuid(); Guid[] includedSurveyedSurfaces = new Guid[] { designUid }; Guid[] includedSurveyedSurfaces2 = new Guid[] { designUid2 }; using (var cache = new TRexSpatialMemoryCache(20000, 1000000, 0.5)) { // Create a context with a included design and validate that a design change causes the appropriate invalidation var testGuid = Guid.NewGuid(); var context = cache.LocateOrCreateContext(projectUid, gridDataType, SpatialCacheFingerprint.ConstructFingerprint(projectUid, gridDataType, null, includedSurveyedSurfaces)); // this content will remain in cache as it uses a different design var context2 = cache.LocateOrCreateContext(projectUid, gridDataType, SpatialCacheFingerprint.ConstructFingerprint(projectUid, gridDataType, null, includedSurveyedSurfaces2)); Assert.True(context.MarkedForRemoval == true, "Empty contents should be marked for removal"); TRexSpatialMemoryCacheContextTests_Element[,] items = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; TRexSpatialMemoryCacheContextTests_Element[,] items2 = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; // Add content to our 2 contexts for (var k = 1; k < 3; k++) { for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { if (k == 1) { items[i, j] = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = (int)(i * SubGridTreeConsts.SubGridTreeDimension), CacheOriginY = (int)(j * SubGridTreeConsts.SubGridTreeDimension), SizeInBytes = 1 }; cache.Add(context, items[i, j], context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);; } else { items2[i, j] = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = (int)(i * SubGridTreeConsts.SubGridTreeDimension), CacheOriginY = (int)(j * SubGridTreeConsts.SubGridTreeDimension), SizeInBytes = 1 }; cache.Add(context2, items2[i, j], context2.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); } } } } // verify items added OK Assert.True(context.MarkedForRemoval == false, "Context should not be marked for removal"); Assert.True(context.TokenCount == 10000, "Token count incorrect after addition"); Assert.True(context2.MarkedForRemoval == false, "Context2 should not be marked for removal"); Assert.True(context2.TokenCount == 10000, "Token count incorrect after addition of context2"); // invalidate first of the 2 contexts added cache.InvalidateDueToDesignChange(projectUid, designUid); cache.MRUList.TokenCount.Should().Be(10000); int counter = 0; for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { cache.MRUList.Get(counter++).Should().NotBe(items[i, j]); } } for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { cache.MRUList.Get(counter++).Should().Be(items2[i, j]); } } //Test context is removed Assert.True(context.MarkedForRemoval == true, "Empty context should be marked for removal"); Assert.True(context.TokenCount == 0, "Token count incorrect after invalidation"); // Test context2 remains Assert.True(context2.MarkedForRemoval == false, "Context2 should not be marked for removal"); Assert.True(context2.TokenCount == 10000, "Token count incorrect after test"); } }
/// <summary> /// Computes a Fingerprint for use in caching surveyed surface height + time responses /// Note: This fingerprint used the SurveyedSurfaceHeightAndTime grid data type in the cache fingerprint, /// even though the core engine returns HeightAndTime results. This allows HeightAndTime and /// SurveyedSurfaceHeightAndTime results to cohabit in the same cache /// </summary> public string CacheFingerprint() { return(SpatialCacheFingerprint.ConstructFingerprint(SiteModelID, GridDataType.SurveyedSurfaceHeightAndTime, null, IncludedSurveyedSurfaces)); }