/// <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> /// Converts IStoreShardMap to ShardMap. /// </summary> /// <param name="manager">Reference to shard map manager.</param> /// <param name="ssm">Storage representation for ShardMap.</param> /// <returns>ShardMap object corresponding to storange representation.</returns> internal static ShardMap CreateShardMapFromStoreShardMap( ShardMapManager manager, IStoreShardMap ssm) { switch (ssm.MapType) { case ShardMapType.List: // Create ListShardMap<TKey> return((ShardMap)Activator.CreateInstance( typeof(ListShardMap <>).MakeGenericType( ShardKey.TypeFromShardKeyType(ssm.KeyType)), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { manager, ssm }, CultureInfo.InvariantCulture)); default: Debug.Assert(ssm.MapType == ShardMapType.Range); // Create RangeShardMap<TKey> return((ShardMap)Activator.CreateInstance( typeof(RangeShardMap <>).MakeGenericType( ShardKey.TypeFromShardKeyType(ssm.KeyType)), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { manager, ssm }, CultureInfo.InvariantCulture)); } }
/// <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> /// 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 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> /// Asynchronously opens a regular <see cref="SqlConnection"/> to the shard /// to which the specified key value is mapped. /// </summary> /// <typeparam name="TKey">Type of the key.</typeparam> /// <param name="key">Input key value.</param> /// <param name="connectionString"> /// Connection string with credential information such as SQL Server credentials or Integrated Security settings. /// The hostname of the server and the database name for the shard are obtained from the lookup operation for key. /// </param> /// <param name="options">Options for validation operations to perform on opened connection.</param> /// <returns>A Task encapsulating an opened SqlConnection.</returns> /// <remarks> /// Note that the <see cref="SqlConnection"/> object returned by this call is not protected against transient faults. /// Callers should follow best practices to protect the connection against transient faults /// in their application code, e.g., by using the transient fault handling /// functionality in the Enterprise Library from Microsoft Patterns and Practices team. /// This call only works if there is a single default mapping. /// </remarks> public Task <SqlConnection> OpenConnectionForKeyAsync <TKey>( TKey key, string connectionString, ConnectionOptions options) { ExceptionUtils.DisallowNullArgument(connectionString, "connectionString"); Debug.Assert(this.StoreShardMap.KeyType != ShardKeyType.None); using (ActivityIdScope activityIdScope = new ActivityIdScope(Guid.NewGuid())) { IShardMapper <TKey> mapper = this.GetMapper <TKey>(); if (mapper == null) { throw new ArgumentException( StringUtils.FormatInvariant( Errors._ShardMap_OpenConnectionForKey_KeyTypeNotSupported, typeof(TKey), this.StoreShardMap.Name, ShardKey.TypeFromShardKeyType(this.StoreShardMap.KeyType)), "key"); } Debug.Assert(mapper != null); return(mapper.OpenConnectionForKeyAsync(key, connectionString, options)); } }
/// <summary> /// Looks up the key value and returns the corresponding mapping. /// </summary> /// <typeparam name="TMapping">Mapping type.</typeparam> /// <typeparam name="TKey">Key type.</typeparam> /// <param name="key">Input key value.</param> /// <param name="useCache">Whether to use cache for lookups.</param> /// <param name="constructMapping">Delegate to construct a mapping object.</param> /// <param name="errorCategory">Category under which errors must be thrown.</param> /// <returns>Mapping that contains the key value.</returns> protected TMapping Lookup <TMapping, TKey>( TKey key, bool useCache, Func <ShardMapManager, ShardMap, IStoreMapping, TMapping> constructMapping, ShardManagementErrorCategory errorCategory) where TMapping : class, IShardProvider { ShardKey sk = new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), key); if (useCache) { ICacheStoreMapping cachedMapping = this.Manager.Cache.LookupMappingByKey(this.ShardMap.StoreShardMap, sk); if (cachedMapping != null) { return(constructMapping(this.Manager, this.ShardMap, cachedMapping.Mapping)); } } // Cache-miss, find mapping for given key in GSM. TMapping m = null; IStoreResults gsmResult; Stopwatch stopwatch = Stopwatch.StartNew(); using (IStoreOperationGlobal op = this.Manager.StoreOperationFactory.CreateFindMappingByKeyGlobalOperation( this.Manager, "Lookup", this.ShardMap.StoreShardMap, sk, CacheStoreMappingUpdatePolicy.OverwriteExisting, errorCategory, true, false)) { gsmResult = op.Do(); } stopwatch.Stop(); Tracer.TraceVerbose( TraceSourceConstants.ComponentNames.BaseShardMapper, "Lookup", "Lookup key from GSM complete; Key type : {0}; Result: {1}; Duration: {2}", typeof(TKey), gsmResult.Result, stopwatch.Elapsed); // If we could not locate the mapping, we return null and do nothing here. if (gsmResult.Result != StoreResult.MappingNotFoundForKey) { return(gsmResult.StoreMappings.Select(sm => constructMapping(this.Manager, this.ShardMap, sm)).Single()); } return(m); }
/// <summary> /// Arguments used to create a point mapping. /// </summary> /// <param name="point">Point value being mapped.</param> /// <param name="shard">Shard used as the mapping target.</param> /// <param name="status">Status of the mapping.</param> public PointMappingCreationInfo(TKey point, Shard shard, MappingStatus status) { ExceptionUtils.DisallowNullArgument(shard, "shard"); this.Value = point; this.Shard = shard; this.Status = status; this.Key = new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), point); }
/// <summary> /// Calculates the hash code for the object. /// </summary> /// <returns>Hash code for the object.</returns> private int CalculateHashCode() { int h; h = ShardKey.QPHash(this.Protocol.GetHashCode(), this.DataSource.ToUpperInvariant().GetHashCode()); h = ShardKey.QPHash(h, this.Port.GetHashCode()); h = ShardKey.QPHash(h, this.Database.ToUpperInvariant().GetHashCode()); return(h); }
/// <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> /// 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> /// 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); } }
public RangeMappingCreationInfo(Range <TKey> value, Shard shard, MappingStatus status) { ExceptionUtils.DisallowNullArgument(value, "value"); ExceptionUtils.DisallowNullArgument(shard, "shard"); this.Value = value; this.Shard = shard; this.Status = status; this.Range = new ShardRange( new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), value.Low), new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), value.HighIsMax ? null : (object)value.High)); }
/// <summary> /// Raise conversion exception. /// </summary> /// <typeparam name="TKey">Key type.</typeparam> /// <param name="ssm">Shard map whose conversion failed.</param> /// <param name="targetKind">Requested type of shard map.</param> private static ShardManagementException GetConversionException <TKey>(IStoreShardMap ssm, string targetKind) { return(new ShardManagementException( ShardManagementErrorCategory.ShardMapManager, ShardManagementErrorCode.ShardMapTypeConversionError, Errors._ShardMapExtensions_AsTypedShardMap_ConversionFailure, ssm.Name, targetKind, typeof(TKey).Name, ssm.MapType.ToString(), ssm.KeyType == ShardKeyType.None ? string.Empty : ShardKey.TypeFromShardKeyType(ssm.KeyType).Name)); }
/// <summary>Returns the intersection of two ranges.</summary> /// <param name="range">Range to intersect with.</param> /// <returns> /// The intersection of the current range and the specified range, null if ranges dont intersect. /// </returns> internal ShardRange Intersect(ShardRange range) { ExceptionUtils.DisallowNullArgument(range, "range"); ShardKey intersectLow = ShardKey.Max(Low, range.Low); ShardKey intersectHigh = ShardKey.Min(High, range.High); if (intersectLow >= intersectHigh) { return(null); } return(new ShardRange(intersectLow, intersectHigh)); }
/// <summary> /// Asynchronously finds the mapping in store for OpenConnectionForKey operation. /// </summary> /// <param name="sk">Key to find.</param> /// <param name="policy">Cache update policy.</param> /// <param name="errorCategory">Error category.</param> /// <returns>Task with the Mapping corresponding to the given key if found as the result.</returns> private async Task <IStoreMapping> LookupMappingForOpenConnectionForKeyAsync( ShardKey sk, CacheStoreMappingUpdatePolicy policy, ShardManagementErrorCategory errorCategory) { IStoreResults gsmResult; Stopwatch stopwatch = Stopwatch.StartNew(); using (IStoreOperationGlobal op = this.Manager.StoreOperationFactory.CreateFindMappingByKeyGlobalOperation( this.Manager, "Lookup", this.ShardMap.StoreShardMap, sk, policy, errorCategory, true, false)) { gsmResult = await op.DoAsync().ConfigureAwait(false); } stopwatch.Stop(); Tracer.TraceVerbose( TraceSourceConstants.ComponentNames.BaseShardMapper, "LookupMappingForOpenConnectionForKeyAsync", "Lookup key from GSM complete; Key type : {0}; Result: {1}; Duration: {2}", sk.DataType, gsmResult.Result, stopwatch.Elapsed); // If we could not locate the mapping, we throw. if (gsmResult.Result == StoreResult.MappingNotFoundForKey) { throw new ShardManagementException( errorCategory, ShardManagementErrorCode.MappingNotFoundForKey, Errors._Store_ShardMapper_MappingNotFoundForKeyGlobal, this.ShardMap.Name, StoreOperationRequestBuilder.SpFindShardMappingByKeyGlobal, "LookupMappingForOpenConnectionForKeyAsync"); } else { return(gsmResult.StoreMappings.Single()); } }
/// <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> /// 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; _mappingsByKey.TryGetValue(key, out cm); if (cm != null) { sm = cm.Mapping; } else { sm = null; } return(cm); }
/// <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> /// Constructs request for obtaining mapping from GSM based on given key. /// </summary> /// <param name="shardMapManager">Shard map manager.</param> /// <param name="operationName">Operation being executed.</param> /// <param name="shardMap">Local shard map.</param> /// <param name="key">Key for lookup operation.</param> /// <param name="policy">Policy for cache update.</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> protected internal FindMappingByKeyGlobalOperation( ShardMapManager shardMapManager, string operationName, IStoreShardMap shardMap, ShardKey key, CacheStoreMappingUpdatePolicy policy, ShardManagementErrorCategory errorCategory, bool cacheResults, bool ignoreFailure) : base(shardMapManager.Credentials, shardMapManager.RetryPolicy, operationName) { _manager = shardMapManager; _shardMap = shardMap; _key = key; _policy = policy; _errorCategory = errorCategory; _cacheResults = cacheResults; _ignoreFailure = ignoreFailure; }
/// <summary> /// Creates a list based <see cref="ListShardMap{TKey}"/>. /// </summary> /// <typeparam name="TKey">Type of keys.</typeparam> /// <param name="shardMapName">Name of shard map.</param> /// <returns>List shard map with the specified name.</returns> public ListShardMap <TKey> CreateListShardMap <TKey>(string shardMapName) { ShardMapManager.ValidateShardMapName(shardMapName); using (ActivityIdScope activityIdScope = new ActivityIdScope(Guid.NewGuid())) { DefaultStoreShardMap dssm = new DefaultStoreShardMap( Guid.NewGuid(), shardMapName, ShardMapType.List, ShardKey.ShardKeyTypeFromType(typeof(TKey))); ListShardMap <TKey> listShardMap = new ListShardMap <TKey>(this, dssm); Tracer.TraceInfo( TraceSourceConstants.ComponentNames.ShardMapManager, "CreateListShardMap", "Start; ShardMap: {0}", shardMapName); Stopwatch stopwatch = Stopwatch.StartNew(); this.AddShardMapToStore("CreateListShardMap", dssm); stopwatch.Stop(); Tracer.TraceInfo( TraceSourceConstants.ComponentNames.ShardMapManager, "CreateListShardMap", "Added ShardMap to Store; ShardMap: {0} Duration: {1}", shardMapName, stopwatch.Elapsed); Tracer.TraceInfo( TraceSourceConstants.ComponentNames.ShardMapManager, "CreateListShardMap", "Complete; ShardMap: {0} Duration: {1}", shardMapName, stopwatch.Elapsed); return(listShardMap); } }
/// <summary> /// Constructs request for obtaining mapping from GSM based on given key. /// </summary> /// <param name="shardMapManager">Shard map manager.</param> /// <param name="operationName">Operation being executed.</param> /// <param name="shardMap">Local shard map.</param> /// <param name="key">Key for lookup operation.</param> /// <param name="policy">Policy for cache update.</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 CreateFindMappingByKeyGlobalOperation( ShardMapManager shardMapManager, string operationName, IStoreShardMap shardMap, ShardKey key, CacheStoreMappingUpdatePolicy policy, ShardManagementErrorCategory errorCategory, bool cacheResults, bool ignoreFailure) { return(new FindMappingByKeyGlobalOperation( shardMapManager, operationName, shardMap, key, policy, errorCategory, cacheResults, ignoreFailure)); }
/// <summary>Constructs a shard range from low boundary (inclusive) to high high boundary (exclusive)</summary> /// <param name="low">Low boundary (inclusive)</param> /// <param name="high">High boundary (exclusive)</param> public ShardRange(ShardKey low, ShardKey high) { ExceptionUtils.DisallowNullArgument(low, "low"); ExceptionUtils.DisallowNullArgument(high, "high"); if (low >= high) { throw new ArgumentOutOfRangeException( "low", low, string.Format( Errors._ShardRange_LowGreaterThanOrEqualToHigh, low, high)); } this.Low = low; this.High = high; this.KeyType = Low.KeyType; _hashCode = this.CalculateHashCode(); }
/// <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> /// Calculates the hash code for the object. /// </summary> /// <returns>Hash code for the object.</returns> private int CalculateHashCode() { return(ShardKey.QPHash(this.Low.GetHashCode(), this.High.GetHashCode())); }
/// <summary>Checks whether the specified key is inside the range.</summary> /// <param name="key">The key to check</param> /// <returns>True if inside, false otherwise</returns> public bool Contains(ShardKey key) { ExceptionUtils.DisallowNullArgument(key, "key"); return((key >= Low) && (key < High)); }
/// <summary> /// Splits the given mapping into 2 at the given key. The new mappings point to the same shard /// as the existing mapping. /// </summary> /// <param name="existingMapping">Given existing mapping.</param> /// <param name="splitAt">Split point.</param> /// <param name="lockOwnerId">Lock owner id of this mapping</param> /// <returns>Read-only collection of 2 new mappings thus created.</returns> internal IReadOnlyList <RangeMapping <TKey> > Split(RangeMapping <TKey> existingMapping, TKey splitAt, Guid lockOwnerId) { this.EnsureMappingBelongsToShardMap <RangeMapping <TKey> >( existingMapping, "Split", "existingMapping"); ShardKey shardKey = new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), splitAt); if (!existingMapping.Range.Contains(shardKey) || existingMapping.Range.Low == shardKey || existingMapping.Range.High == shardKey) { throw new ArgumentOutOfRangeException( "splitAt", Errors._ShardMapping_SplitPointOutOfRange); } IStoreShard newShard = new DefaultStoreShard( existingMapping.Shard.StoreShard.Id, Guid.NewGuid(), existingMapping.ShardMapId, existingMapping.Shard.StoreShard.Location, existingMapping.Shard.StoreShard.Status); IStoreMapping mappingToRemove = new DefaultStoreMapping( existingMapping.StoreMapping.Id, existingMapping.StoreMapping.ShardMapId, newShard, existingMapping.StoreMapping.MinValue, existingMapping.StoreMapping.MaxValue, existingMapping.StoreMapping.Status, existingMapping.StoreMapping.LockOwnerId); IStoreMapping[] mappingsToAdd = new IStoreMapping[2] { new DefaultStoreMapping( Guid.NewGuid(), newShard.ShardMapId, newShard, existingMapping.Range.Low.RawValue, shardKey.RawValue, (int)existingMapping.Status, lockOwnerId), new DefaultStoreMapping( Guid.NewGuid(), newShard.ShardMapId, newShard, shardKey.RawValue, existingMapping.Range.High.RawValue, (int)existingMapping.Status, lockOwnerId) }; using (IStoreOperation op = this.Manager.StoreOperationFactory.CreateReplaceMappingsOperation( this.Manager, StoreOperationCode.SplitMapping, this.ShardMap.StoreShardMap, new[] { new Tuple <IStoreMapping, Guid>(mappingToRemove, lockOwnerId) }, mappingsToAdd.Select(mappingToAdd => new Tuple <IStoreMapping, Guid>(mappingToAdd, lockOwnerId)).ToArray())) { op.Do(); } return(mappingsToAdd .Select(m => new RangeMapping <TKey>(this.Manager, this.ShardMap, m)) .ToList() .AsReadOnly()); }
/// <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> /// Given a key value, asynchronously obtains a SqlConnection to the shard in the mapping /// that contains the key value. /// </summary> /// <typeparam name="TMapping">Mapping type.</typeparam> /// <typeparam name="TKey">Key type.</typeparam> /// <param name="key">Input key value.</param> /// <param name="constructMapping">Delegate to construct a mapping object.</param> /// <param name="errorCategory">Error category.</param> /// <param name="connectionString"> /// Connection string with credential information, the DataSource and Database are /// obtained from the results of the lookup operation for key. /// </param> /// <param name="secureCredential">Secure SQL Credential.</param> /// <param name="options">Options for validation operations to perform on opened connection.</param> /// <returns>A task encapsulating an opened SqlConnection as the result.</returns> protected async Task <SqlConnection> OpenConnectionForKeyAsync <TMapping, TKey>( TKey key, Func <ShardMapManager, ShardMap, IStoreMapping, TMapping> constructMapping, ShardManagementErrorCategory errorCategory, string connectionString, SqlCredential secureCredential, ConnectionOptions options = ConnectionOptions.Validate) where TMapping : class, IShardProvider { ShardKey sk = new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), key); // Try to find the mapping within the cache. ICacheStoreMapping csm = this.Manager.Cache.LookupMappingByKey(this.ShardMap.StoreShardMap, sk); IStoreMapping sm; if (csm != null) { sm = csm.Mapping; } else { sm = await this.LookupMappingForOpenConnectionForKeyAsync( sk, CacheStoreMappingUpdatePolicy.OverwriteExisting, errorCategory).ConfigureAwait(false); } SqlConnection result; bool lookupMappingOnEx = false; CacheStoreMappingUpdatePolicy cacheUpdatePolicyOnEx = CacheStoreMappingUpdatePolicy.OverwriteExisting; try { // Initially attempt to connect based on lookup results from either cache or GSM. result = await this.ShardMap.OpenConnectionAsync( constructMapping(this.Manager, this.ShardMap, sm), connectionString, secureCredential, options).ConfigureAwait(false); csm.ResetTimeToLiveIfNecessary(); return(result); } catch (ShardManagementException smme) { // If we hit a validation failure due to stale version of mapping, we will perform one more attempt. if (((options & ConnectionOptions.Validate) == ConnectionOptions.Validate) && smme.ErrorCategory == ShardManagementErrorCategory.Validation && smme.ErrorCode == ShardManagementErrorCode.MappingDoesNotExist) { // Assumption here is that this time the attempt should succeed since the cache entry // has already been either evicted, or updated based on latest data from the server. lookupMappingOnEx = true; cacheUpdatePolicyOnEx = CacheStoreMappingUpdatePolicy.OverwriteExisting; } else { // The error was not due to validation but something else e.g. // 1) Shard map does not exist // 2) Mapping could not be found. throw; } } catch (SqlException) { // We failed to connect. If we were trying to connect from an entry in cache and mapping expired in cache. if (csm != null && csm.HasTimeToLiveExpired()) { using (IdLock _idLock = new IdLock(csm.Mapping.StoreShard.Id)) { // Similar to DCL pattern, we need to refresh the mapping again to see if we still need to go to the store // to lookup the mapping after acquiring the shard lock. It might be the case that a fresh version has already // been obtained by some other thread. csm = this.Manager.Cache.LookupMappingByKey(this.ShardMap.StoreShardMap, sk); // Only go to store if the mapping is stale even after refresh. if (csm == null || csm.HasTimeToLiveExpired()) { // Refresh the mapping in cache. And try to open the connection after refresh. lookupMappingOnEx = true; cacheUpdatePolicyOnEx = CacheStoreMappingUpdatePolicy.UpdateTimeToLive; } else { sm = csm.Mapping; } } } else { // Either: // 1) The mapping is still within the TTL. No refresh. // 2) Mapping was not in cache, we originally did a lookup for mapping in GSM and even then could not connect. throw; } } if (lookupMappingOnEx) { sm = await this.LookupMappingForOpenConnectionForKeyAsync( sk, cacheUpdatePolicyOnEx, errorCategory).ConfigureAwait(false); } // One last attempt to open the connection after a cache refresh result = await this.ShardMap.OpenConnectionAsync( constructMapping(this.Manager, this.ShardMap, sm), connectionString, secureCredential, options).ConfigureAwait(false); // Reset TTL on successful connection. csm.ResetTimeToLiveIfNecessary(); return(result); }