/// <summary> /// Add or update a mapping in cache. /// </summary> /// <param name="sm">Storage mapping object.</param> /// <param name="policy">Policy to use for preexisting cache entries during update.</param> internal override void AddOrUpdate(IStoreMapping sm, CacheStoreMappingUpdatePolicy policy) { ShardKey min = ShardKey.FromRawValue(this.KeyType, sm.MinValue); // Make range out of mapping key ranges. ShardRange range = new ShardRange( min, ShardKey.FromRawValue(this.KeyType, sm.MaxValue)); CacheMapping cm; ICacheStoreMapping csm; IStoreMapping smDummy; // We need to update TTL and update entry if: // a) We are in update TTL mode // b) Mapping exists and same as the one we already have // c) Entry is beyond the TTL limit if (policy == CacheStoreMappingUpdatePolicy.UpdateTimeToLive && (csm = this.LookupByKey(min, out smDummy)) != null && csm.Mapping.Id == sm.Id /*&& * TimerUtils.ElapsedMillisecondsSince(csm.CreationTime) >= csm.TimeToLiveMilliseconds */) { cm = new CacheMapping(sm, CacheMapper.CalculateNewTimeToLiveMilliseconds(csm)); } else { cm = new CacheMapping(sm); } this.Remove(sm); // Add the entry to lookup table by Range. _mappingsByRange.Add(range, cm); }
/// <summary> /// Performs binary search on the cached mappings and returns the /// index of mapping object whose min-value is less than and closest /// to given key value. /// </summary> /// <param name="key">Input key.</param> /// <returns>Index of range in the cache which contains the given key.</returns> private int GetIndexOfMappingWithClosestMaxGreaterThanOrEqualToMaxKey(ShardKey key) { IList <ShardRange> rangeKeys = _mappingsByRange.Keys; int lb = 0; int ub = rangeKeys.Count - 1; while (lb <= ub) { int mid = lb + (ub - lb) / 2; ShardRange current = rangeKeys[mid]; if (current.High > key) { if (current.Low <= key) { return(mid); } else { ub = mid - 1; } } else { lb = mid + 1; } } return(lb); }
/// <summary> /// Performs binary search on the cached mappings and returns the /// index of mapping object which contains the given key. /// </summary> /// <param name="key">Input key.</param> /// <returns>Index of range in the cache which contains the given key.</returns> private int GetIndexOfMappingContainingShardKey(ShardKey key) { IList <ShardRange> rangeKeys = _mappingsByRange.Keys; int lb = 0; int ub = rangeKeys.Count - 1; while (lb <= ub) { int mid = lb + (ub - lb) / 2; ShardRange current = rangeKeys[mid]; if (current.Contains(key)) { return(mid); } else if (key < current.Low) { ub = mid - 1; } else { lb = mid + 1; } } return(-1); }
/// <summary> /// Looks up a mapping by key. /// </summary> /// <param name="key">Key value.</param> /// <param name="sm">Storage mapping object.</param> /// <returns>Mapping object which has the key value.</returns> internal override ICacheStoreMapping LookupByKey(ShardKey key, out IStoreMapping sm) { CacheMapping cm; // Performs a binary search in the ranges for key value and // then return the result. int rangeIndex = this.GetIndexOfMappingContainingShardKey(key); if (rangeIndex != -1) { ShardRange range = _mappingsByRange.Keys[rangeIndex]; cm = _mappingsByRange[range]; // DEVNOTE(wbasheer): We should clone the mapping. sm = cm.Mapping; } else { cm = null; sm = null; } return(cm); }
/// <summary> /// Constructs request for obtaining all the mappings from GSM based on given shard and mappings. /// </summary> /// <param name="shardMapManager">Shard map manager.</param> /// <param name="operationName">Operation being executed.</param> /// <param name="shardMap">Local shard map.</param> /// <param name="shard">Local shard.</param> /// <param name="range">Optional range to get mappings from.</param> /// <param name="errorCategory">Error category.</param> /// <param name="cacheResults">Whether to cache the results of the operation.</param> /// <param name="ignoreFailure">Ignore shard map not found error.</param> internal GetMappingsByRangeGlobalOperation(ShardMapManager shardMapManager, string operationName, IStoreShardMap shardMap, IStoreShard shard, ShardRange range, ShardManagementErrorCategory errorCategory, bool cacheResults, bool ignoreFailure) : base(shardMapManager.Credentials, shardMapManager.RetryPolicy, operationName) { _manager = shardMapManager; _shardMap = shardMap; _shard = shard; _range = range; _errorCategory = errorCategory; _cacheResults = cacheResults; _ignoreFailure = ignoreFailure; }
/// <summary> /// Constructs range based on its low boundary value. The low boundary value is /// set to the one specified in <paramref name="low"/> while the /// high boundary value is set to maximum possible value i.e. +infinity. /// </summary> /// <param name="low">Low boundary value (inclusive).</param> public Range(TKey low) { ShardKeyType k = ShardKey.ShardKeyTypeFromType(typeof(TKey)); _r = new ShardRange( new ShardKey(k, low), new ShardKey(k, null)); this.Low = low; this.HighIsMax = true; }
/// <summary> /// Constructs range based on its low and high boundary values. /// </summary> /// <param name="low">Low boundary value (inclusive).</param> /// <param name="high">High boundary value (exclusive).</param> public Range(TKey low, TKey high) { ShardKeyType k = ShardKey.ShardKeyTypeFromType(typeof(TKey)); _r = new ShardRange( new ShardKey(k, low), new ShardKey(k, high)); this.Low = low; this.High = high; }
/// <summary> /// Converts a given shard range to xml. /// </summary> /// <param name="range">Input range.</param> /// <returns>XElement representing the given range.</returns> internal static XElement WriteShardRange(ShardRange range) { if (range == null) { return(new XElement("Range", new XAttribute("Null", 1))); } else { return(new XElement("Range", new XAttribute("Null", 0), new XElement("MinValue", StringUtils.ByteArrayToString(range.Low.RawValue)), new XElement("MaxValue", new XAttribute("Null", range.High.IsMax ? 1 : 0), range.High.IsMax ? null : StringUtils.ByteArrayToString(range.High.RawValue)))); } }
/// <summary> /// Gets all the mappings that exist within given range. /// </summary> /// <param name="range">Optional range value, if null, we cover everything.</param> /// <param name="shard">Optional shard parameter, if null, we cover all shards.</param> /// <param name="constructMapping">Delegate to construct a mapping object.</param> /// <param name="errorCategory">Category under which errors will be posted.</param> /// <param name="mappingType">Name of mapping type.</param> /// <returns>Read-only collection of mappings that overlap with given range.</returns> protected IReadOnlyList <TMapping> GetMappingsForRange <TMapping, TKey>( Range <TKey> range, Shard shard, Func <ShardMapManager, ShardMap, IStoreMapping, TMapping> constructMapping, ShardManagementErrorCategory errorCategory, string mappingType) where TMapping : class { ShardRange sr = null; if (shard != null) { ExceptionUtils.EnsureShardBelongsToShardMap( this.Manager, this.ShardMap, shard, "GetMappings", mappingType); } if (range != null) { sr = new ShardRange( new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), range.Low), new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), range.HighIsMax ? null : (object)range.High)); } IStoreResults result; using (IStoreOperationGlobal op = this.Manager.StoreOperationFactory.CreateGetMappingsByRangeGlobalOperation( this.Manager, "GetMappingsForRange", this.ShardMap.StoreShardMap, shard != null ? shard.StoreShard : null, sr, errorCategory, true, // Always cache. false)) { result = op.Do(); } return(result.StoreMappings .Select(sm => constructMapping(this.Manager, this.ShardMap, sm)) .ToList() .AsReadOnly()); }
/// <summary> /// Constructs request for obtaining all the shard maps and shards from an LSM. /// </summary> /// <param name="shardMapManager">Shard map manager.</param> /// <param name="location">Location of the LSM.</param> /// <param name="operationName">Operation name.</param> /// <param name="shardMap">Local shard map.</param> /// <param name="shard">Local shard.</param> /// <param name="range">Optional range to get mappings from.</param> /// <param name="ignoreFailure">Ignore shard map not found error.</param> internal GetMappingsByRangeLocalOperation( ShardMapManager shardMapManager, ShardLocation location, string operationName, IStoreShardMap shardMap, IStoreShard shard, ShardRange range, bool ignoreFailure) : base(shardMapManager.Credentials, shardMapManager.RetryPolicy, location, operationName) { Debug.Assert(shard != null); _shardMap = shardMap; _shard = shard; _range = range; _ignoreFailure = ignoreFailure; }
/// <summary> /// Constructs request for obtaining all the shard maps and shards from an LSM. /// </summary> /// <param name="shardMapManager">Shard map manager.</param> /// <param name="location">Location of the LSM.</param> /// <param name="operationName">Operation name.</param> /// <param name="shardMap">Local shard map.</param> /// <param name="shard">Local shard.</param> /// <param name="range">Optional range to get mappings from.</param> /// <param name="ignoreFailure">Ignore shard map not found error.</param> public virtual IStoreOperationLocal CreateGetMappingsByRangeLocalOperation( ShardMapManager shardMapManager, ShardLocation location, string operationName, IStoreShardMap shardMap, IStoreShard shard, ShardRange range, bool ignoreFailure) { return(new GetMappingsByRangeLocalOperation( shardMapManager, location, operationName, shardMap, shard, range, ignoreFailure)); }
/// <summary> /// Constructs request for obtaining all the mappings from GSM based on given shard and mappings. /// </summary> /// <param name="shardMapManager">Shard map manager.</param> /// <param name="operationName">Operation being executed.</param> /// <param name="shardMap">Local shard map.</param> /// <param name="shard">Local shard.</param> /// <param name="range">Optional range to get mappings from.</param> /// <param name="errorCategory">Error category.</param> /// <param name="cacheResults">Whether to cache the results of the operation.</param> /// <param name="ignoreFailure">Ignore shard map not found error.</param> /// <returns>The store operation.</returns> public virtual IStoreOperationGlobal CreateGetMappingsByRangeGlobalOperation( ShardMapManager shardMapManager, string operationName, IStoreShardMap shardMap, IStoreShard shard, ShardRange range, ShardManagementErrorCategory errorCategory, bool cacheResults, bool ignoreFailure) { return(new GetMappingsByRangeGlobalOperation( shardMapManager, operationName, shardMap, shard, range, errorCategory, cacheResults, ignoreFailure)); }
/// <summary> /// Finds all mappings to be purged based on the given input ranges. /// </summary> /// <param name="ts">LSM transaction scope.</param> /// <returns>Mappings which are to be removed.</returns> private IEnumerable <IStoreMapping> GetMappingsToPurge(IStoreTransactionScope ts) { IEnumerable <IStoreMapping> lsmMappings = null; IStoreResults result; if (_rangesToRemove == null) { // If no ranges are specified, get all the mappings for the shard. result = ts.ExecuteOperation( StoreOperationRequestBuilder.SpGetAllShardMappingsLocal, StoreOperationRequestBuilder.GetAllShardMappingsLocal(_shardMap, _shard, null)); if (result.Result != StoreResult.Success) { // Possible errors are: // StoreResult.ShardMapDoesNotExist // StoreResult.StoreVersionMismatch // StoreResult.MissingParametersForStoredProcedure throw StoreOperationErrorHandler.OnRecoveryErrorLocal( result, _shardMap, this.Location, ShardManagementErrorCategory.Recovery, this.OperationName, StoreOperationRequestBuilder.SpGetAllShardMappingsLocal); } lsmMappings = result.StoreMappings; } else { // If any ranges are specified, only delete intersected ranges. IDictionary <ShardRange, IStoreMapping> mappingsToPurge = new Dictionary <ShardRange, IStoreMapping>(); foreach (ShardRange range in _rangesToRemove) { switch (_shardMap.MapType) { case ShardMapType.Range: result = ts.ExecuteOperation( StoreOperationRequestBuilder.SpGetAllShardMappingsLocal, StoreOperationRequestBuilder.GetAllShardMappingsLocal( _shardMap, _shard, range)); break; default: Debug.Assert(_shardMap.MapType == ShardMapType.List); result = ts.ExecuteOperation( StoreOperationRequestBuilder.SpFindShardMappingByKeyLocal, StoreOperationRequestBuilder.FindShardMappingByKeyLocal( _shardMap, ShardKey.FromRawValue(_shardMap.KeyType, range.Low.RawValue))); break; } if (result.Result != StoreResult.Success) { if (result.Result != StoreResult.MappingNotFoundForKey) { // Possible errors are: // StoreResult.ShardMapDoesNotExist // StoreResult.StoreVersionMismatch // StoreResult.MissingParametersForStoredProcedure throw StoreOperationErrorHandler.OnRecoveryErrorLocal( result, _shardMap, this.Location, ShardManagementErrorCategory.Recovery, this.OperationName, _shardMap.MapType == ShardMapType.Range ? StoreOperationRequestBuilder.SpGetAllShardMappingsLocal : StoreOperationRequestBuilder.SpFindShardMappingByKeyLocal); } else { // No intersections being found is fine. Skip to the next mapping. Debug.Assert(_shardMap.MapType == ShardMapType.List); } } else { foreach (IStoreMapping mapping in result.StoreMappings) { ShardRange intersectedRange = new ShardRange( ShardKey.FromRawValue(_shardMap.KeyType, mapping.MinValue), ShardKey.FromRawValue(_shardMap.KeyType, mapping.MaxValue)); mappingsToPurge[intersectedRange] = mapping; } } } lsmMappings = mappingsToPurge.Values; } return(lsmMappings); }
/// <summary> /// Remove a mapping object from cache. /// </summary> /// <param name="sm">Storage maping object.</param> /// <remarks> /// Q: Do we ever need to remove multiple entries from the cache which cover the same range? /// A: Yes. Imagine that you have some stale mapping in the cache, user just simply performs /// an AddRangeMapping operation on a subset of stale mapping range, now you should remove the /// stale mapping. /// </remarks> internal override void Remove(IStoreMapping sm) { ShardKey minKey = ShardKey.FromRawValue(this.KeyType, sm.MinValue); ShardKey maxKey = ShardKey.FromRawValue(this.KeyType, sm.MaxValue); // Make range out of mapping key. ShardRange range = new ShardRange(minKey, maxKey); // Fast code path, where cache does contain the exact range. if (_mappingsByRange.ContainsKey(range)) { _mappingsByRange.Remove(range); } else { int indexMin = this.GetIndexOfMappingWithClosestMinLessThanOrEqualToMinKey(minKey); int indexMax = this.GetIndexOfMappingWithClosestMaxGreaterThanOrEqualToMaxKey(maxKey); if (indexMin < 0) { indexMin = 0; } if (indexMax >= _mappingsByRange.Keys.Count) { indexMax = _mappingsByRange.Keys.Count - 1; } // Find first range with max greater than min key. for (; indexMin <= indexMax; indexMin++) { ShardRange currentRange = _mappingsByRange.Keys[indexMin]; if (currentRange.High > minKey) { break; } } // Find first range with min less than or equal to max key. for (; indexMax >= indexMin; indexMax--) { ShardRange currentRange = _mappingsByRange.Keys[indexMax]; if (currentRange.Low <= maxKey) { break; } } List <ShardRange> rangesToRemove = new List <ShardRange>(); for (; indexMin <= indexMax; indexMin++) { rangesToRemove.Add(_mappingsByRange.Keys[indexMin]); } foreach (ShardRange rangeToRemove in rangesToRemove) { _mappingsByRange.Remove(rangeToRemove); } } }