/// <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) { // Make key out of mapping key. ShardKey key = ShardKey.FromRawValue(this.KeyType, sm.MinValue); CacheMapping cm; // 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 && _mappingsByKey.TryGetValue(key, out cm) && cm.Mapping.Id == sm.Id /*&& * TimerUtils.ElapsedMillisecondsSince(cm.CreationTime) >= cm.TimeToLiveMilliseconds */) { cm = new CacheMapping(sm, CacheMapper.CalculateNewTimeToLiveMilliseconds(cm)); } else { cm = new CacheMapping(sm); } // Remove existing entry. this.Remove(sm); // Add the entry to lookup table by Key. _mappingsByKey.Add(key, cm); }
/// <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> /// Remove a mapping object from cache. /// </summary> /// <param name="sm">Storage maping object.</param> internal override void Remove(IStoreMapping sm) { // Make key value out of mapping key. ShardKey key = ShardKey.FromRawValue(this.KeyType, sm.MinValue); // Remove existing entry. if (_mappingsByKey.ContainsKey(key)) { _mappingsByKey.Remove(key); } }
/// <summary> /// Internal constructor used for deserialization from store representation of /// the mapping object. /// </summary> /// <param name="manager">Owning ShardMapManager.</param> /// <param name="shardMap">Owning shard map.</param> /// <param name="mapping">Storage representation of the mapping.</param> internal PointMapping( ShardMapManager manager, ShardMap shardMap, IStoreMapping mapping) { Debug.Assert(manager != null); this.Manager = manager; Debug.Assert(mapping != null); Debug.Assert(mapping.ShardMapId != default(Guid)); Debug.Assert(mapping.StoreShard.ShardMapId != default(Guid)); this.StoreMapping = mapping; _shard = new Shard(this.Manager, shardMap, mapping.StoreShard); this.Key = ShardKey.FromRawValue(ShardKey.ShardKeyTypeFromType(typeof(TKey)), mapping.MinValue); this.Value = (TKey)this.Key.Value; }
/// <summary> /// Internal constructor used for deserialization from store representation of /// the mapping object. /// </summary> /// <param name="manager">Owning ShardMapManager.</param> /// <param name="shardMap">Owning shard map.</param> /// <param name="mapping">Storage representation of the mapping.</param> internal RangeMapping( ShardMapManager manager, ShardMap shardMap, IStoreMapping mapping) { Debug.Assert(manager != null); this.Manager = manager; Debug.Assert(mapping != null); Debug.Assert(mapping.ShardMapId != default(Guid)); Debug.Assert(mapping.StoreShard.ShardMapId != default(Guid)); this.StoreMapping = mapping; _shard = new Shard(this.Manager, shardMap, mapping.StoreShard); this.Range = new ShardRange( ShardKey.FromRawValue(ShardKey.ShardKeyTypeFromType(typeof(TKey)), mapping.MinValue), ShardKey.FromRawValue(ShardKey.ShardKeyTypeFromType(typeof(TKey)), mapping.MaxValue)); this.Value = this.Range.High.IsMax ? new Range <TKey>(this.Range.Low.GetValue <TKey>()) : new Range <TKey>(this.Range.Low.GetValue <TKey>(), this.Range.High.GetValue <TKey>()); }
/// <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); } } }
/// <summary> /// Finds all mappings to be purged based on the given input ranges. /// </summary> /// <param name="ts">GSM transaction scope.</param> /// <returns>Mappings which are to be removed.</returns> private IEnumerable <IStoreMapping> GetMappingsToPurge(IStoreTransactionScope ts) { // Find all the mappings in GSM belonging to the shard IStoreResults gsmMappingsByShard = ts.ExecuteOperation( StoreOperationRequestBuilder.SpGetAllShardMappingsGlobal, StoreOperationRequestBuilder.GetAllShardMappingsGlobal(_shardMap, _shard, null)); if (gsmMappingsByShard.Result != StoreResult.Success) { // Possible errors are: // StoreResult.ShardMapDoesNotExist // StoreResult.StoreVersionMismatch // StoreResult.MissingParametersForStoredProcedure throw StoreOperationErrorHandler.OnRecoveryErrorGlobal( gsmMappingsByShard, _shardMap, _shard, ShardManagementErrorCategory.Recovery, this.OperationName, StoreOperationRequestBuilder.SpGetAllShardMappingsGlobal); } IDictionary <ShardRange, IStoreMapping> intersectingMappings = new Dictionary <ShardRange, IStoreMapping>(); foreach (IStoreMapping gsmMappingByShard in gsmMappingsByShard.StoreMappings) { ShardKey min = ShardKey.FromRawValue( _shardMap.KeyType, gsmMappingByShard.MinValue); ShardKey max = null; switch (_shardMap.MapType) { case ShardMapType.Range: max = ShardKey.FromRawValue( _shardMap.KeyType, gsmMappingByShard.MaxValue); break; default: Debug.Assert(_shardMap.MapType == ShardMapType.List); max = ShardKey.FromRawValue( _shardMap.KeyType, gsmMappingByShard.MinValue).GetNextKey(); break; } intersectingMappings.Add(new ShardRange(min, max), gsmMappingByShard); } // We need to discover, also, the range of intersecting mappings, so we can transitively detect // inconsistencies with other shards. foreach (IStoreMapping lsmMapping in _mappingsToRemove) { ShardKey min = ShardKey.FromRawValue(_shardMap.KeyType, lsmMapping.MinValue); IStoreResults gsmMappingsByRange; switch (_shardMap.MapType) { case ShardMapType.Range: gsmMappingsByRange = ts.ExecuteOperation( StoreOperationRequestBuilder.SpGetAllShardMappingsGlobal, StoreOperationRequestBuilder.GetAllShardMappingsGlobal( _shardMap, null, new ShardRange( min, ShardKey.FromRawValue(_shardMap.KeyType, lsmMapping.MaxValue)))); break; default: Debug.Assert(_shardMap.MapType == ShardMapType.List); gsmMappingsByRange = ts.ExecuteOperation( StoreOperationRequestBuilder.SpFindShardMappingByKeyGlobal, StoreOperationRequestBuilder.FindShardMappingByKeyGlobal( _shardMap, min)); break; } if (gsmMappingsByRange.Result != StoreResult.Success) { if (gsmMappingsByRange.Result != StoreResult.MappingNotFoundForKey) { // Possible errors are: // StoreResult.ShardMapDoesNotExist // StoreResult.StoreVersionMismatch // StoreResult.MissingParametersForStoredProcedure throw StoreOperationErrorHandler.OnRecoveryErrorGlobal( gsmMappingsByRange, _shardMap, _shard, ShardManagementErrorCategory.Recovery, this.OperationName, _shardMap.MapType == ShardMapType.Range ? StoreOperationRequestBuilder.SpGetAllShardMappingsGlobal : StoreOperationRequestBuilder.SpFindShardMappingByKeyGlobal); } else { // No intersections being found is fine. Skip to the next mapping. Debug.Assert(_shardMap.MapType == ShardMapType.List); } } else { foreach (IStoreMapping gsmMappingByRange in gsmMappingsByRange.StoreMappings) { ShardKey minGlobal = ShardKey.FromRawValue(_shardMap.KeyType, gsmMappingByRange.MinValue); ShardKey maxGlobal = null; switch (_shardMap.MapType) { case ShardMapType.Range: maxGlobal = ShardKey.FromRawValue(_shardMap.KeyType, gsmMappingByRange.MaxValue); break; default: Debug.Assert(_shardMap.MapType == ShardMapType.List); maxGlobal = ShardKey.FromRawValue(_shardMap.KeyType, gsmMappingByRange.MinValue).GetNextKey(); break; } intersectingMappings[new ShardRange(minGlobal, maxGlobal)] = gsmMappingByRange; } } } return(intersectingMappings.Values); }