/// <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(); } }
/// <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)); }
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); }
/// <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); }
/// <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); }
/// <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; } } }