public override void SetRowState(ObjectState rs) { using (new WriterLock(_lock)) { if (_rowState != rs) { CEFDebug.WriteInfo($"RowState={rs}, {_source?.GetBaseType().Name}", _source); _rowState = rs; if (rs == ObjectState.Unchanged) { _isBagChanged = false; } } } }
public IDBProviderCommand ExecuteNoResultSet() { CEFDebug.DumpSQLCall(_cmd.CommandText, this.GetParameterValues()); try { _cmd.ExecuteNonQuery(); } catch (Exception ex) { CEFDebug.WriteInfo($"SQL Call Error: " + ex.Message); throw; } return(this); }
protected override void OnPropertyChanged(string propName, object?oldVal, object?newVal, bool isBag) { if (propName[0] != KeyService.SHADOW_PROP_PREFIX) { base.OnPropertyChanged(propName, oldVal, newVal, isBag); Globals.GlobalPropertyChangePreview?.Invoke(this, propName, oldVal, newVal); if (SafeGetState() == ObjectState.Unchanged) { if (!Globals.GlobalPropertiesExcludedFromDirtyCheck.Contains(propName)) { #if DEBUG if (CEFDebug.DebugEnabled) { CEFDebug.WriteInfo($"PropChange: {propName}, {oldVal}, {newVal}"); } #endif SetRowState(ObjectState.Modified); DirtyStateChange?.Invoke(this, new DirtyStateChangeEventArgs(ObjectState.Modified)); } } } }
/// <summary> /// For cases where persisting to file, ensures items that are eligible to be cached to disk actually are persisted to disk. /// </summary> /// <param name="honorShutdown"></param> private void SaveUncommitted(bool honorShutdown) { if (DefaultStorageStrategy == CacheStorageStrategy.OnlyMemory) { return; } if (Interlocked.Exchange(ref _savingCommitted, 1) > 0) { return; } try { List <MFSEntry> items = new List <MFSEntry>(); foreach (var i in _index) { if (honorShutdown && Interlocked.Read(ref _stopping) > 0) { return; } lock (i.ObjSync) { if (!i.Persisted && i.Active && !string.IsNullOrEmpty(i.FileName) && (i.Properties != null || i.Rows != null) && i.ExpiryDate > DateTime.Now) { items.Add(i); } } } Parallel.ForEach(items, new ParallelOptions() { MaxDegreeOfParallelism = honorShutdown ? Environment.ProcessorCount > 4 ? Environment.ProcessorCount >> 2 : 1 : -1 }, (i, pls) => { try { if (honorShutdown && Interlocked.Read(ref _stopping) > 0) { pls.Break(); return; } if (i.Properties != null) { SaveProperties(_formatter, Path.Combine(RootDirectory, i.FileName), i.Properties); } else { if (i.Rows != null) { SaveRows(_formatter, Path.Combine(RootDirectory, i.FileName), i.Rows); } } lock (i.ObjSync) { i.Persisted = true; } } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache save uncommitted: {ex.Message}"); } }); } finally { Interlocked.Exchange(ref _savingCommitted, 0); } }
public void AddByQuery <T>(IEnumerable <T> list, string text, object[] parms = null, int?expirySeconds = null, CacheBehavior?mode = null) where T : class, new() { var ss = CEF.CurrentServiceScope; if (mode == null) { mode = ss.ResolvedCacheBehaviorForType(typeof(T)); } if ((mode & CacheBehavior.QueryBased) == 0 && (mode & CacheBehavior.ConvertQueryToIdentity) != 0 && ((mode & CacheBehavior.ForAllDoesntConvertToIdentity) == 0 || string.Compare(text, "All", true) != 0)) { // All we can do is for all list items, add to identity cache void act2() { try { Interlocked.Increment(ref _working); Parallel.ForEach(list, (i) => { using (CEF.UseServiceScope(ss)) { AddByIdentity <T>(i, expirySeconds: expirySeconds); } }); } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache serializer: {ex.Message}"); } finally { Interlocked.Decrement(ref _working); } } if (Globals.AsyncCacheUpdates) { Task.Factory.StartNew(act2); } else { act2(); } return; } if ((mode & CacheBehavior.OnlyForAllQuery) != 0 && string.Compare(text, "All", true) != 0) { return; } StringBuilder sb = new StringBuilder(128); sb.Append(typeof(T).Name); sb.Append(text.ToUpperInvariant()); if (parms != null) { foreach (var k in parms) { sb.Append(k); } } string hash; using (SHA256Managed hasher = new SHA256Managed()) { hash = Convert.ToBase64String(hasher.ComputeHash(Encoding.ASCII.GetBytes(sb.ToString()))); } var c = _index.GetFirstByName(nameof(MFSEntry.ByQuerySHA), hash); if (!expirySeconds.HasValue) { expirySeconds = CEF.CurrentServiceScope.ResolvedCacheDurationForType(typeof(T)); } var newExpDate = DateTime.Now.AddSeconds(expirySeconds.GetValueOrDefault(DefaultCacheIntervalSeconds)); if (c == null) { c = new MFSEntry(); if (list.Any()) { c.ObjectTypeName = list.First().GetBaseType().Name; } else { c.ObjectTypeName = typeof(T).Name; } c.ByQuerySHA = hash; c.FileName = BuildNewFileName(typeof(T)); c.QueryForAll = string.Compare(text, "All", true) == 0; _index.Add(c); } long current; lock (c.ObjSync) { current = ++c.Sequence; c.ExpiryDate = newExpDate; c.SourceList = list; c.Active = true; } void act() { try { Interlocked.Increment(ref _working); using (CEF.UseServiceScope(ss)) { // Process all items in parallel, building a list we'll turn into json but also potentially caching "by identity" per row ConcurrentBag <IDictionary <string, object> > rows = new ConcurrentBag <IDictionary <string, object> >(); var aiw = list.AllAsInfraWrapped().ToArray(); Parallel.ForEach(aiw, (iw) => { using (CEF.UseServiceScope(ss)) { rows.Add(iw.GetAllValues(true, true)); if ((mode & CacheBehavior.ConvertQueryToIdentity) != 0 && ((mode & CacheBehavior.ForAllDoesntConvertToIdentity) == 0 || string.Compare(text, "All", true) != 0)) { var uw = iw.AsUnwrapped() as T; if (uw != null) { AddByIdentity <T>(uw, expirySeconds: expirySeconds); } } } }); lock (c.ObjSync) { if (c.Sequence == current) { c.Rows = rows; c.SourceList = null; } } } } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache serializer: {ex.Message}"); // Invalidate the entry is probably the safest c.Active = false; c.Properties = null; c.Rows = null; } finally { Interlocked.Decrement(ref _working); } } if (Globals.AsyncCacheUpdates) { Task.Factory.StartNew(act); } else { act(); } }
private void _monitor_Elapsed(object sender, ElapsedEventArgs e) { if (Interlocked.Read(ref _stopping) > 0) { return; } // Will actually use a background thread that's low priority Thread bwt = new Thread(() => { if (Interlocked.Read(ref _stopping) > 0) { return; } try { Interlocked.Increment(ref _working); if (Interlocked.Read(ref _working) > 1) { return; } var d = DateTime.Now; if (DefaultStorageStrategy != CacheStorageStrategy.OnlyMemory) { try { Parallel.ForEach(GetAllFiles(RootDirectory), new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount > 4 ? Environment.ProcessorCount >> 2 : 1 }, (f) => { if (_index.GetFirstByName(nameof(MFSEntry.FileName), f) == null) { if (File.Exists(f)) { File.Delete(f); } } }); } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache monitor: {ex.Message}"); } } Cleanup(true); _lastMonitorTimerDuration = DateTime.Now.Subtract(d).TotalMilliseconds; if (MonitorTimerUseDynamicInterval) { MonitorTimerMillisecondInterval = Convert.ToInt32(Math.Pow(_lastMonitorTimerDuration.GetValueOrDefault() + (_working * 5), 0.9)); if (MonitorTimerMillisecondInterval < 200) { MonitorTimerMillisecondInterval *= 3; } else { if (MonitorTimerMillisecondInterval > 50000) { MonitorTimerMillisecondInterval /= 2; } } _monitor.Interval = MonitorTimerMillisecondInterval; } } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache monitor: {ex.Message}"); } finally { Interlocked.Decrement(ref _working); _monitor.Start(); } }); bwt.Priority = ThreadPriority.Lowest; bwt.Start(); }
/// <summary> /// Performs evictions based on expiry dates, cache size, etc. then ensures the persisted cache index is updated. /// </summary> /// <param name="honorShutdown"></param> private void Cleanup(bool honorShutdown) { SaveUncommitted(honorShutdown); if (honorShutdown && Interlocked.Read(ref _stopping) > 0) { return; } // In final cleanup don't worry about evictions if (honorShutdown) { DateTime d = DateTime.Now; var remove = (from a in _index where !a.Active || a.ExpiryDate < d || (!string.IsNullOrEmpty(a.FileName) && !File.Exists(Path.Combine(RootDirectory, a.FileName))) select a).ToList(); if ((_index.Count - remove.Count) > MaximumItemCount) { var discard = MaximumItemCount - _index.Count + remove.Count; remove.AddRange((from a in _index where a.Active orderby a.ReadCount ascending, a.LastRead select a).Take(discard)); } if (Interlocked.Read(ref _stopping) > 0) { return; } Parallel.ForEach(remove, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount > 4 ? Environment.ProcessorCount >> 2: 1 }, (c, pls) => { if (Interlocked.Read(ref _stopping) > 0) { pls.Break(); return; } try { if (!string.IsNullOrEmpty(c.FileName) && File.Exists(Path.Combine(RootDirectory, c.FileName))) { File.Delete(Path.Combine(RootDirectory, c.FileName)); } _index.Remove(c); } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache cleanup: {ex.Message}"); } }); if (Interlocked.Read(ref _stopping) > 0) { return; } } SaveIndex(honorShutdown); }
/// <summary> /// Reads any existing cache index file, building the in-memory representation, without bringing in actual item data until actually requested. /// </summary> private void RestoreIndex() { if (DefaultStorageStrategy == CacheStorageStrategy.OnlyMemory) { return; } try { var fn = Path.Combine(RootDirectory, "index.json"); if (File.Exists(fn)) { using (var jr = new JsonTextReader(new StreamReader(File.OpenRead(fn)))) { // Should be start array jr.Read(); while (jr.Read() && jr.TokenType != JsonToken.EndArray) { // Should be start object jr.Read(); var i = new MFSEntry(); i.FileName = jr.ReadAsString(); jr.Read(); i.ObjectTypeName = jr.ReadAsString(); jr.Read(); i.ByQuerySHA = jr.ReadAsString(); jr.Read(); i.QueryForAll = jr.ReadAsBoolean().GetValueOrDefault(); jr.Read(); i.ByIdentityComposite = jr.ReadAsString(); jr.Read(); i.ExpiryDate = jr.ReadAsDateTime().GetValueOrDefault(); jr.Read(); if (i.ExpiryDate > DateTime.Now) { i.Active = true; i.Persisted = true; _index.Add(i); } } } } else { FlushAll(RootDirectory); _index.Clear(); } } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache restore index: {ex.Message}"); // Any problems, go with an empty cache FlushAll(RootDirectory); _index.Clear(); } }