Ejemplo n.º 1
0
        /// <summary>
        /// Looks up a given key in given shard map.
        /// </summary>
        /// <param name="shardMap">Storage representation of shard map.</param>
        /// <param name="key">Key value.</param>
        /// <returns>Mapping corresponding to <paramref name="key"/> or null.</returns>
        public virtual ICacheStoreMapping LookupMappingByKey(IStoreShardMap shardMap, ShardKey key)
        {
            ICacheStoreMapping sm = null;

            using (ReadLockScope rls = _cacheRoot.GetReadLockScope(false))
            {
                CacheShardMap csm = _cacheRoot.LookupById(shardMap.Id);

                if (csm != null)
                {
                    using (ReadLockScope rlsShardMap = csm.GetReadLockScope(false))
                    {
                        IStoreMapping smDummy;
                        sm = csm.Mapper.LookupByKey(key, out smDummy);

                        /*
                         *          // perf counter can not be updated in csm.Mapper.LookupByKey() as this function is also called from csm.Mapper.AddOrUpdate()
                         *          // so updating perf counter value here instead.
                         *          csm.IncrementPerformanceCounter(sm == null ? PerformanceCounterName.MappingsLookupFailedPerSec : PerformanceCounterName.MappingsLookupSucceededPerSec);
                         */
                    }
                }
            }

            return(sm);
        }
 /// <summary>
 /// Resets the mapping entry expiration time to 0 if necessary
 /// </summary>
 /// <param name="csm"></param>
 internal static void ResetTimeToLiveIfNecessary(this ICacheStoreMapping csm)
 {
     // Reset TTL on successful connection.
     if (csm != null && csm.TimeToLiveMilliseconds > 0)
     {
         csm.ResetTimeToLive();
     }
 }
Ejemplo n.º 3
0
        /// <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);
        }
        protected static long CalculateNewTimeToLiveMilliseconds(ICacheStoreMapping csm)
        {
            if (csm.TimeToLiveMilliseconds <= 0)
            {
                return(5000);
            }

            // Exponentially increase the time up to a limit of 30 seconds, after which we keep
            // returning 30 seconds as the TTL for the mapping entry.
            return(Math.Min(30000, csm.TimeToLiveMilliseconds * 2));
        }
Ejemplo n.º 5
0
        protected static long CalculateNewTimeToLiveMilliseconds(ICacheStoreMapping csm)
        {
            if (csm.TimeToLiveMilliseconds <= 0)
            {
                return 5000;
            }

            // Exponentially increase the time up to a limit of 30 seconds, after which we keep
            // returning 30 seconds as the TTL for the mapping entry.
            return Math.Min(30000, csm.TimeToLiveMilliseconds * 2);
        }
        public override ICacheStoreMapping LookupMappingByKey(IStoreShardMap shardMap, ShardKey key)
        {
            this.LookupMappingCount++;

            ICacheStoreMapping result = base.LookupMappingByKey(shardMap, key);

            if (result == null)
            {
                this.LookupMappingMissCount++;
            }
            else
            {
                this.LookupMappingHitCount++;
            }

            return(result);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Looks up a given key in given shard map.
        /// </summary>
        /// <param name="shardMap">Storage representation of shard map.</param>
        /// <param name="key">Key value.</param>
        /// <returns>Mapping corresponding to <paramref name="key"/> or null.</returns>
        public virtual ICacheStoreMapping LookupMappingByKey(IStoreShardMap shardMap, ShardKey key)
        {
            ICacheStoreMapping sm = null;

            using (ReadLockScope rls = _cacheRoot.GetReadLockScope(false))
            {
                CacheShardMap csm = _cacheRoot.LookupById(shardMap.Id);

                if (csm != null)
                {
                    using (ReadLockScope rlsShardMap = csm.GetReadLockScope(false))
                    {
                        IStoreMapping smDummy;
                        sm = csm.Mapper.LookupByKey(key, out smDummy);
                    }
                }
            }

            return(sm);
        }
Ejemplo n.º 8
0
        /// <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);
        }
Ejemplo n.º 9
0
        /// <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="lookupOptions">Whether to use cache and/or storage 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,
            LookupOptions lookupOptions,
            Func <ShardMapManager, ShardMap, IStoreMapping, TMapping> constructMapping,
            ShardManagementErrorCategory errorCategory)
            where TMapping : class, IShardProvider
        {
            ShardKey sk = new ShardKey(ShardKey.ShardKeyTypeFromType(typeof(TKey)), key);

            // Try to lookup in cache. Note the interation with cache removal later in this method.
            bool tryLookupInCache = lookupOptions.HasFlag(LookupOptions.LookupInCache);

            if (tryLookupInCache)
            {
                ICacheStoreMapping cachedMapping = this.Manager.Cache.LookupMappingByKey(this.ShardMap.StoreShardMap, sk);
                if (cachedMapping != null)
                {
                    return(constructMapping(this.Manager, this.ShardMap, cachedMapping.Mapping));
                }
            }

            // Cache-miss (or didn't use cache), find mapping for given key in GSM.
            if (lookupOptions.HasFlag(LookupOptions.LookupInStore))
            {
                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 mapping was found, return it.
                if (gsmResult.Result != StoreResult.MappingNotFoundForKey)
                {
                    return(gsmResult.StoreMappings.Select(sm => constructMapping(this.Manager, this.ShardMap, sm)).Single());
                }

                // If we could not locate the mapping, then we might need to update the cache to remove it.
                //
                // Only do this if we didn't already try to return the mapping from the cache (since, if it was found,
                // we would have already returned it earlier).
                if (!tryLookupInCache)
                {
                    ICacheStoreMapping cachedMapping =
                        this.Manager.Cache.LookupMappingByKey(this.ShardMap.StoreShardMap, sk);
                    if (cachedMapping != null)
                    {
                        this.Manager.Cache.DeleteMapping(cachedMapping.Mapping);
                    }
                }
            }

            // Mapping not found - return null
            return(null);
        }
        /// <summary>
        /// Given a key value, 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="connectionInfo">
        /// Connection info 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>An opened SqlConnection.</returns>
        protected SqlConnection OpenConnectionForKey <TMapping, TKey>(
            TKey key,
            Func <ShardMapManager, ShardMap, IStoreMapping, TMapping> constructMapping,
            ShardManagementErrorCategory errorCategory,
            SqlConnectionInfo connectionInfo,
            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 = this.LookupMappingForOpenConnectionForKey(
                    sk,
                    CacheStoreMappingUpdatePolicy.OverwriteExisting,
                    errorCategory);
            }

            SqlConnection result;

            try
            {
                // Initially attempt to connect based on lookup results from either cache or GSM.
                result = this.ShardMap.OpenConnection(
                    constructMapping(this.Manager, this.ShardMap, sm),
                    connectionInfo,
                    options);

                // Reset TTL on successful connection.
                if (csm != null && csm.TimeToLiveMilliseconds > 0)
                {
                    csm.ResetTimeToLive();
                }

                this.Manager.Cache.IncrementPerformanceCounter(this.ShardMap.StoreShardMap, PerformanceCounterName.DdrOperationsPerSec);
                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.
                    sm = this.LookupMappingForOpenConnectionForKey(
                        sk,
                        CacheStoreMappingUpdatePolicy.OverwriteExisting,
                        errorCategory);

                    result = this.ShardMap.OpenConnection(
                        constructMapping(this.Manager, this.ShardMap, sm),
                        connectionInfo,
                        options);
                    this.Manager.Cache.IncrementPerformanceCounter(this.ShardMap.StoreShardMap, PerformanceCounterName.DdrOperationsPerSec);
                    return(result);
                }
                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 && TimerUtils.ElapsedMillisecondsSince(csm.CreationTime) >= csm.TimeToLiveMilliseconds)
                {
                    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 || TimerUtils.ElapsedMillisecondsSince(csm.CreationTime) >= csm.TimeToLiveMilliseconds)
                        {
                            // Refresh the mapping in cache. And try to open the connection after refresh.
                            sm = this.LookupMappingForOpenConnectionForKey(
                                sk,
                                CacheStoreMappingUpdatePolicy.UpdateTimeToLive,
                                errorCategory);
                        }
                        else
                        {
                            sm = csm.Mapping;
                        }
                    }

                    result = this.ShardMap.OpenConnection(
                        constructMapping(this.Manager, this.ShardMap, sm),
                        connectionInfo,
                        options);

                    // Reset TTL on successful connection.
                    if (csm != null && csm.TimeToLiveMilliseconds > 0)
                    {
                        csm.ResetTimeToLive();
                    }

                    this.Manager.Cache.IncrementPerformanceCounter(this.ShardMap.StoreShardMap, PerformanceCounterName.DdrOperationsPerSec);
                    return(result);
                }
                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;
                }
            }
        }