/// <summary> /// Locates the sub grid in the sub grid tree that contains the cell identified by CellX and CellY in the global /// sub grid tree cell address space. The tree level for the sub grid returned is specified in Level. /// </summary> public static ISubGrid LocateSubGridContaining(IStorageProxy storageProxyForSubGrids, IServerSubGridTree forSubGridTree, //const GridDataCache : TICDataStoreCache; int cellX, int cellY, byte level, bool lookInCacheOnly, bool acceptSpeculativeReadFailure) { IServerLeafSubGrid leafSubGrid = null; var createdANewSubGrid = false; ISubGrid result = null; if (forSubGridTree == null) { throw new TRexSubGridProcessingException($"Sub grid tree null in {nameof(LocateSubGridContaining)}"); } // Note: Sub grid tree specific interlocks are no longer used. The tree now internally // manages fine grained locks across structurally mutating activities such as node/leaf // sub grid addition and reading content from the persistent store. // First check to see if the requested cell is present in a leaf sub grid var subGrid = forSubGridTree.LocateClosestSubGridContaining(cellX, cellY, level); if (subGrid == null) // Something bad happened { _log.LogWarning($"Failed to locate sub grid at {cellX}:{cellY}, level {level}, data model ID:{forSubGridTree.ID}"); return(null); } if (!subGrid.IsLeafSubGrid() && !lookInCacheOnly && level == forSubGridTree.NumLevels) { if (forSubGridTree.CachingStrategy == ServerSubGridTreeCachingStrategy.CacheSubGridsInTree) { // Create the leaf sub grid that will be used to read in the sub grid from the disk. // In the case where the sub grid isn't present on the disk this reference will be destroyed subGrid = forSubGridTree.ConstructPathToCell(cellX, cellY, Types.SubGridPathConstructionType.CreateLeaf); } else if (forSubGridTree.CachingStrategy == ServerSubGridTreeCachingStrategy.CacheSubGridsInIgniteGridCache) { // Create the leaf sub grid without constructing elements in the grid to represent it other than the // path in the tree to the parent of the sub grid. // Note: Setting owner and parent relationship from the sub grid to the tree in this fashion permits // business logic in th sub grid that require knowledge of it parent and owner relationships to function // correctly while not including a reference to the sub grid from the tree. subGrid = forSubGridTree.CreateNewSubGrid(forSubGridTree.NumLevels); subGrid.SetAbsoluteOriginPosition(cellX & ~SubGridTreeConsts.SubGridLocalKeyMask, cellY & ~SubGridTreeConsts.SubGridLocalKeyMask); subGrid.Owner = forSubGridTree; subGrid.Parent = forSubGridTree.ConstructPathToCell(cellX, cellY, Types.SubGridPathConstructionType.CreatePathToLeaf); } if (subGrid != null) { createdANewSubGrid = true; } else { _log.LogError($"Failed to create leaf sub grid in LocateSubGridContaining for sub grid at {cellX}x{cellY}"); return(null); } } if (subGrid.IsLeafSubGrid()) { leafSubGrid = subGrid as IServerLeafSubGrid; } if (leafSubGrid == null) // Something bad happened { _log.LogError($"Sub grid request result for {cellX}:{cellY} is not a leaf sub grid, it is a {subGrid.GetType().Name}."); return(null); } if (!createdANewSubGrid) { if (lookInCacheOnly) { if (subGrid.Level == level) { return(subGrid); } // If the returned sub grid is a leaf sub grid then it was already present in the // cache. If the level of the returned sub grid matches the request level parameter // then there is nothing more to do here. if (subGrid.IsLeafSubGrid() && ((leafSubGrid.HasSubGridDirectoryDetails || leafSubGrid.Dirty) && leafSubGrid.HasAllCellPasses() && leafSubGrid.HasLatestData()) || (!subGrid.IsLeafSubGrid() && subGrid.Level == level)) { return(subGrid); } } } if ((!leafSubGrid.HasSubGridDirectoryDetails && !leafSubGrid.Dirty) || !(leafSubGrid.HasAllCellPasses() && leafSubGrid.HasLatestData())) { // The requested cell is either not present in the sub grid tree (cache), // or it is residing on disk, and a newly created sub grid has been constructed // to contain the data read from disk. // The underlying assumption is that this method is only called if the caller knows // that the sub grid exists in the sub grid tree (this is known via the sub grid existence // map available to the caller). In cases where eventual consistency // may mean that a sub grid was removed from the sub grid tree since the caller retrieved // its copy of the sub grid existence map this function will fail gracefully with a null sub grid. // The exception to this rule is the tag file processor service which may speculatively // attempt to read a sub grid that doesn't exist. // This is a different approach to desktop systems where the individual node sub grids // contain mini existence maps for the sub grids below them. if (forSubGridTree.LoadLeafSubGrid(storageProxyForSubGrids, new SubGridCellAddress(cellX, cellY), true, true, leafSubGrid)) { // We've loaded it - get the reference to the new sub grid and return it result = leafSubGrid; } else { // The sub grid could not be loaded. This is likely due to it not ever existing // in the model, or it may have been deleted. Failure here does not necessarily // constitute evidence of corruption in the data model. Examination of the // spatial existence map in conjunction with the requested sub grid index is // required to determine that. Advise the caller nothing was read by sending back // a null sub grid reference. // The failed sub grid is not proactively deleted and will remain so the normal cache // expiry mechanism can remove it in its normal operations if (acceptSpeculativeReadFailure) { // Return the otherwise empty sub grid back to the caller and integrate it into the cache if (_log.IsTraceEnabled()) { _log.LogTrace($"Speculative read failure accepted for sub grid {leafSubGrid.Moniker()}. Blank sub grid returned to caller."); } result = leafSubGrid; } else { _log.LogWarning($"Failed to read leaf sub grid {leafSubGrid.Moniker()} in model {forSubGridTree.ID}. Failed sub grid is NOT removed from the tree"); // Empty the sub grid leaf based data to encourage it to be read on a secondary attempt leafSubGrid.DeAllocateLeafFullPassStacks(); leafSubGrid.DeAllocateLeafLatestPassGrid(); } } } // Ignite special case - allow Dirty leaf sub grids to be returned if (result == null) { if (leafSubGrid.HasSubGridDirectoryDetails && leafSubGrid.Dirty && leafSubGrid.HasAllCellPasses() && leafSubGrid.HasLatestData()) { result = leafSubGrid; } } // IGNITE: Last gasp - if the sub grid is in memory and has directory details then just return it if (result == null && leafSubGrid.HasSubGridDirectoryDetails) { result = leafSubGrid; } return(result); }