/// <summary> /// Constructs cached representation of a mapping object. /// </summary> /// <param name="storeMapping">Storage representation of mapping.</param> /// <param name="timeToLiveMilliseconds">Mapping expiration time.</param> internal CacheMapping(IStoreMapping storeMapping, long timeToLiveMilliseconds) { this.Mapping = storeMapping; this.CreationTime = TimerUtils.GetTimestamp(); this.TimeToLiveMilliseconds = timeToLiveMilliseconds; }
/// <summary> /// Whether TimeToLiveMilliseconds have elapsed /// since the CreationTime /// </summary> /// <returns>True if they have</returns> public bool HasTimeToLiveExpired() { return(TimerUtils.ElapsedMillisecondsSince(this.CreationTime) >= this.TimeToLiveMilliseconds); }
/// <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="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>An opened SqlConnection.</returns> protected SqlConnection OpenConnectionForKey <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 = 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), connectionString, 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), connectionString, 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), connectionString, 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; } } }