Example #1
0
        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;
                    }
                }
            }
        }
Example #2
0
        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);
        }
Example #3
0
        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();
            }
        }