/// <summary> /// Gets by the tenant identifier. /// </summary> private TColl GetByTenantId(Guid tenantId) { // Check if it exists; if so then return. if (_dict.TryGetValue(tenantId, out CacheValue <TColl> cv) && !cv.Policy.HasExpired()) { return(cv.Value); } // Lock against the key to minimise concurrent gets (which could be expensive). lock (_keyLock.Lock(tenantId)) { if (_dict.TryGetValue(tenantId, out cv) && !cv.Policy.HasExpired()) { return(cv.Value); } var coll = GetCollectionInternal(); var policy = (ICachePolicy)GetPolicy().Clone(); policy.Reset(); cv = _dict.GetOrAdd(tenantId, new CacheValue <TColl> { Policy = policy, Value = coll }); return(cv.Value); } }
public bool Match(CartRuleContext context, RuleExpression expression) { var sessionKey = context.SessionKey; var lockKey = "rule:cart:carttotalrule:" + sessionKey.ToString(); if (KeyedLock.IsLockHeld(lockKey)) { return(false); } // We must prevent the rule from indirectly calling itself. It would cause a stack overflow on cart page. using (KeyedLock.Lock(lockKey)) { var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); var cartTotal = ((decimal?)_orderTotalCalculationService.GetShoppingCartTotal(cart)) ?? decimal.Zero; // Currency values must be rounded, otherwise unexpected results may occur. var money = new Money(cartTotal, context.WorkContext.WorkingCurrency); cartTotal = money.RoundedAmount; var result = expression.Operator.Match(cartTotal, expression.Value); return(result); } }
public bool Match(CartRuleContext context, RuleExpression expression) { var lockKey = $"rule:cart:cartsubtotalrule:{Thread.CurrentThread.ManagedThreadId}-{expression.Id}"; if (KeyedLock.IsLockHeld(lockKey)) { //$"locked: {lockKey}".Dump(); return(false); } // We must prevent the rule from indirectly calling itself. It would cause a stack overflow on cart page. using (KeyedLock.Lock(lockKey)) { var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); _orderTotalCalculationService.GetShoppingCartSubTotal(cart, out _, out _, out var cartSubtotal, out _); // Currency values must be rounded, otherwise unexpected results may occur. var money = new Money(cartSubtotal, context.WorkContext.WorkingCurrency); cartSubtotal = money.RoundedAmount; var result = expression.Operator.Match(cartSubtotal, expression.Value); //$"unlocked {result}: {lockKey}".Dump(); return(result); } }
public T Get <T>(string key, Func <T> acquirer, TimeSpan?duration = null, bool independent = false) { if (TryGet(key, independent, out T value)) { return(value); } if (_scopeAccessor.Value.HasScope(key)) { throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key)); } // Get the (semaphore) locker specific to this key using (KeyedLock.Lock("cache:" + key, TimeSpan.FromMinutes(1))) { // Atomic operation must be outer locked if (!TryGet(key, independent, out value)) { using (_scopeAccessor.Value.BeginScope(key)) { value = acquirer(); Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); return(value); } } } return(value); }
public void LockOnSameKey2() { var kl = new KeyedLock <int>(); var cd = new ConcurrentDictionary <int, int>(); var inLock = false; Parallel.ForEach(new int[] { 1, 1, 1, 1, 1, 1 }, (key) => { TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} X"); kl.Lock <int>(key, () => { Assert.IsFalse(inLock); inLock = true; TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XX"); System.Threading.Thread.Sleep(10); TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XXX"); inLock = false; return(key); }); TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XXXX"); }); }
public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable <string> virtualPathDependencies, string content, params string[] processorCodes) { if (!_isEnabled) { return(null); } Guard.NotEmpty(virtualPath, nameof(virtualPath)); Guard.NotEmpty(content, nameof(content)); var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) { CacheFolder.CreateDirectory(cacheDirectoryName); try { // Save main content file // TODO: determine correct file extension CreateFileFromEntries(cacheDirectoryName, "asset.css", new[] { content }); // Save dependencies file var deps = ResolveVirtualPathDependencies(virtualPath, virtualPathDependencies, themeName); CreateFileFromEntries(cacheDirectoryName, "asset.dependencies", deps); // Save hash file var currentHash = BundleTable.VirtualPathProvider.GetFileHash(virtualPath, deps); CreateFileFromEntries(cacheDirectoryName, "asset.hash", new[] { currentHash }); // Save codes file CreateFileFromEntries(cacheDirectoryName, "asset.pcodes", processorCodes); var entry = new CachedAssetEntry { Content = content, HashCode = currentHash, OriginalVirtualPath = virtualPath, VirtualPathDependencies = deps, PhysicalPath = CacheFolder.MapPath(cacheDirectoryName), ThemeName = themeName, StoreId = storeId, ProcessorCodes = processorCodes }; SetupEvictionObserver(entry); Logger.DebugFormat("Succesfully inserted asset '{0}' to cache.", virtualPath); return(entry); } catch (Exception ex) { Logger.ErrorFormat(ex, "Error while inserting asset '{0}' to the asset cache.", virtualPath); InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); } } return(null); }
public T Get <T>(string key, Func <T> acquirer, TimeSpan?duration = null, bool independent = false, bool allowRecursion = false) { if (TryGet(key, independent, out T value)) { return(value); } if (!allowRecursion && _scopeAccessor.Value.HasScope(key)) { throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key)); } // Get the (semaphore) locker specific to this key using (KeyedLock.Lock("cache:" + key, TimeSpan.FromSeconds(5))) { // Atomic operation must be outer locked if (!TryGet(key, independent, out value)) { var scope = !allowRecursion?_scopeAccessor.Value.BeginScope(key) : ActionDisposable.Empty; using (scope) { value = acquirer(); var dependencies = !allowRecursion ? _scopeAccessor.Value.Current?.Dependencies : (IEnumerable <string>)null; Put(key, value, duration, dependencies); return(value); } } } return(value); }
public virtual DbCacheEntry Put(string key, object value, IEnumerable <string> dependentEntitySets, TimeSpan?duration) { if (!Enabled || IsToxic(dependentEntitySets)) { return(null); } key = HashKey(key); using (KeyedLock.Lock(key)) { var entitySets = dependentEntitySets.Distinct().ToArray(); var entry = new DbCacheEntry { Key = key, Value = value, EntitySets = entitySets, CachedOnUtc = DateTime.UtcNow, Duration = duration }; _cache.Put(key, entry); foreach (var entitySet in entitySets) { var lookup = GetLookupSet(entitySet); lookup.Add(key); } return(entry); } }
public virtual bool TryGet(string key, out object value) { value = null; if (!Enabled) { return(false); } key = HashKey(key); var now = DateTime.UtcNow; var entry = _cache.Get <DbCacheEntry>(key, independent: true); if (entry != null) { if (entry.HasExpired(now)) { using (KeyedLock.Lock(key)) { InvalidateItemUnlocked(entry); } } else { value = entry.Value; return(true); } } return(false); }
public virtual void InvalidateItem(string key) { if (!Enabled) { return; } Guard.NotEmpty(key, nameof(key)); using (KeyedLock.Lock(key)) { InvalidateItemUnlocked(key); } }
public bool InvalidateAsset(string virtualPath) { Guard.NotEmpty(virtualPath, nameof(virtualPath)); var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) { if (CacheFolder.DirectoryExists(cacheDirectoryName)) { return(InvalidateAssetInternal(cacheDirectoryName, themeName, storeId)); } return(false); } }
public string Build(BundleType type, IEnumerable <string> files) { if (files == null || !files.Any()) { return(string.Empty); } var bundleVirtualPath = this.GetBundleVirtualPath(type, files); var bundleFor = BundleTable.Bundles.GetBundleFor(bundleVirtualPath); if (bundleFor == null) { using (KeyedLock.Lock("BundleBuilder.Build." + bundleVirtualPath)) { bundleFor = BundleTable.Bundles.GetBundleFor(bundleVirtualPath); if (bundleFor == null) { var nullOrderer = new NullOrderer(); Bundle bundle = (type == BundleType.Script) ? new CustomScriptBundle(bundleVirtualPath) as Bundle : new SmartStyleBundle(bundleVirtualPath) as Bundle; bundle.Orderer = nullOrderer; bundle.Include(files.ToArray()); BundleTable.Bundles.Add(bundle); } } } if (type == BundleType.Script) { return(Scripts.Render(bundleVirtualPath).ToString()); //// Uncomment this if you want to bypass asset caching on mobile browsers //return Scripts.RenderFormat("<script src='{0}?" + CommonHelper.GenerateRandomDigitCode(5) + "'></script>", // files.Select(x => VirtualPathUtility.ToAppRelative(x)).ToArray()).ToString(); } return(Styles.Render(bundleVirtualPath).ToString()); }
public void LockOnDifferentKey2() { var kl = new KeyedLock <int>(); var cd = new ConcurrentDictionary <int, int>(); var inLock1 = false; var inLock2 = false; Parallel.ForEach(new int[] { 1, 2, 1, 2, 1, 2 }, (key) => { TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} X"); kl.Lock <int>(key, () => { if (key == 1) { Assert.IsFalse(inLock1); inLock1 = true; TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XX"); System.Threading.Thread.Sleep(10); TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XXX"); inLock1 = false; } else { Assert.IsFalse(inLock2); inLock2 = true; TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XX"); System.Threading.Thread.Sleep(10); TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XXX"); inLock2 = false; } return(key); }); TestContext.WriteLine($"{System.Threading.Thread.CurrentThread.ManagedThreadId} XXXX"); }); }
public CachedAssetEntry GetAsset(string virtualPath) { if (!_isEnabled) { return(null); } Guard.NotEmpty(virtualPath, nameof(virtualPath)); var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); if (CacheFolder.DirectoryExists(cacheDirectoryName)) { try { var deps = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.dependencies")); var hash = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.hash")); if (!TryValidate(virtualPath, deps, hash, out IEnumerable <string> parsedDeps, out string currentHash)) { Logger.DebugFormat("Invalidating cached asset for '{0}' because it is not valid anymore.", virtualPath); InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); return(null); } // TODO: determine correct file extension var content = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.css")); if (content == null) { using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) { InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); return(null); } } var codes = ParseFileContent(CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.pcodes"))); var entry = new CachedAssetEntry { Content = content, HashCode = currentHash, OriginalVirtualPath = virtualPath, VirtualPathDependencies = parsedDeps, PhysicalPath = CacheFolder.MapPath(cacheDirectoryName), ThemeName = themeName, StoreId = storeId, ProcessorCodes = codes.ToArray() }; SetupEvictionObserver(entry); Logger.DebugFormat("Succesfully read asset '{0}' from cache.", virtualPath); return(entry); } catch (Exception ex) { Logger.ErrorFormat(ex, "Error while resolving asset '{0}' from the asset cache.", virtualPath); } } return(null); }
private IAsset TranslateInternal(IAsset asset) { IAsset result; var chronometer = EngineContext.Current.Resolve <IChronometer>(); using (chronometer.Step("Translate asset {0}".FormatInvariant(asset.VirtualPath))) { bool validationMode = ThemeHelper.IsStyleValidationRequest(); if (validationMode || !TryGetCachedAsset(asset, out result)) { using (KeyedLock.Lock("CachedAsset:" + asset.VirtualPath)) { if (validationMode || !TryGetCachedAsset(asset, out result)) { using (chronometer.Step("Compile asset {0}".FormatInvariant(asset.VirtualPath))) { result = _inner.Translate(asset); var cachedAsset = new CachedAsset { AssetTypeCode = AssetTypeCode.Css, IsStylesheet = true, Minified = result.Minified, Combined = result.Combined, Content = result.Content, OriginalAssets = asset.OriginalAssets, VirtualPath = asset.VirtualPath, VirtualPathDependencies = result.VirtualPathDependencies, Url = asset.Url }; result = AssetTranslorUtil.PostProcessAsset(cachedAsset, this.IsDebugMode); if (!validationMode) { var pCodes = new List <string>(3); if (cachedAsset.Minified) { pCodes.Add(DefaultAssetCache.MinificationCode); } if (cachedAsset.RelativePathsResolved) { pCodes.Add(DefaultAssetCache.UrlRewriteCode); } if (cachedAsset.Autoprefixed) { pCodes.Add(DefaultAssetCache.AutoprefixCode); } AssetCache.InsertAsset( cachedAsset.VirtualPath, cachedAsset.VirtualPathDependencies, cachedAsset.Content, pCodes.ToArray()); } } } } } } return(result); }