/// <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>
        /// 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);
        }
示例#3
0
        /// <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;
        }
示例#4
0
        /// <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;
        }
        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>
        /// 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>
        /// 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>
        /// 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>
        /// 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>());
        }
示例#10
0
        /// <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>
        /// 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="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,
            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,
                    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,
                options).ConfigureAwait(false);

            // Reset TTL on successful connection.
            csm.ResetTimeToLiveIfNecessary();

            return(result);
        }