<commit_before>﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.Caching;
using System.Threading;
using Aggregates.Contracts;
using Aggregates.Extensions;
using NServiceBus.Logging;

namespace Aggregates.Internal
{
    public class MemoryStreamCache : IStreamCache, IDisposable
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(MemoryStreamCache));
        private static readonly MemoryCache MemCache = new MemoryCache("Aggregates Cache", new NameValueCollection { { "CacheMemoryLimitMegabytes", "500" } });
        // For streams that are changing multiple times a second, no sense to cache them if they get immediately evicted
        private static readonly HashSet<string> Uncachable = new HashSet<string>();
        private static readonly HashSet<string> LevelOne = new HashSet<string>();
        private static readonly HashSet<string> LevelZero = new HashSet<string>();
        private static int _stage;
        private static readonly Timer _unachableEviction = new Timer(_ =>
        {
            // Clear uncachable every 10 minutes
            if (_stage == 120)
            {
                _stage = 0;
                Logger.Write(LogLevel.Debug, () => $"Clearing {Uncachable.Count} uncachable stream names");
                Uncachable.Clear();
            }
            // Clear levelOne every minute
            if (_stage % 12 == 0)
                LevelOne.Clear();

            // Clear levelZero every 5 seconds
            LevelZero.Clear();

            _stage++;
        }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));

        private readonly bool _intelligent;
        private bool _disposed;

        public MemoryStreamCache(/*Boolean Intelligent = false*/)
        {
            _intelligent = true;
        }

        public void Cache(string stream, object cached)
        {
            if (_intelligent && (Uncachable.Contains(stream) || LevelOne.Contains(stream)))
                return;

            MemCache.Set(stream, cached, new CacheItemPolicy { AbsoluteExpiration = DateTime.UtcNow + TimeSpan.FromMinutes(5) });
        }
        public void Evict(string stream)
        {
            if (_intelligent)
            {
                if (Uncachable.Contains(stream)) return;

                if (LevelZero.Contains(stream))
                {
                    if (LevelOne.Contains(stream))
                    {
                        Logger.Write(LogLevel.Info, () => $"Stream {stream} has been evicted frequenty, marking uncachable for a few minutes");
                        Uncachable.Add(stream);
                    }
                    else
                        LevelOne.Add(stream);
                }
                else
                    LevelZero.Add(stream);

            }
            MemCache.Remove(stream);
        }
        public object Retreive(string stream)
        {
            var cached = MemCache.Get(stream);

            return cached;
        }

        public bool Update(string stream, object payload)
        {
            var cached = MemCache.Get(stream);

            if (!(cached is IEventStream) || !(payload is IWritableEvent)) return false;

            Logger.DebugFormat("Updating cached stream [{0}]", stream);
            var real = (cached as IEventStream);<commit_msg>Making stream cache more verbose<commit_after>﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Runtime.Caching;
using System.Threading;
using Aggregates.Contracts;
using Aggregates.Extensions;
using NServiceBus.Logging;

namespace Aggregates.Internal
{
    public class MemoryStreamCache : IStreamCache, IDisposable
    {
        private static readonly ILog Logger = LogManager.GetLogger(typeof(MemoryStreamCache));
        private static readonly MemoryCache MemCache = new MemoryCache("Aggregates Cache", new NameValueCollection { { "CacheMemoryLimitMegabytes", "500" } });
        // For streams that are changing multiple times a second, no sense to cache them if they get immediately evicted
        private static readonly HashSet<string> Uncachable = new HashSet<string>();
        private static readonly HashSet<string> LevelOne = new HashSet<string>();
        private static readonly HashSet<string> LevelZero = new HashSet<string>();
        private static int _stage;
        private static readonly Timer _unachableEviction = new Timer(_ =>
        {
            // Clear uncachable every 10 minutes
            if (_stage == 120)
            {
                _stage = 0;
                Logger.Write(LogLevel.Debug, () => $"Clearing {Uncachable.Count} uncachable stream names");
                Uncachable.Clear();
            }
            // Clear levelOne every minute
            if (_stage % 12 == 0)
                LevelOne.Clear();

            // Clear levelZero every 5 seconds
            LevelZero.Clear();

            _stage++;
        }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));

        private readonly bool _intelligent;
        private bool _disposed;

        public MemoryStreamCache(/*Boolean Intelligent = false*/)
        {
            _intelligent = false;
        }

        public void Cache(string stream, object cached)
        {
            if (_intelligent && (Uncachable.Contains(stream) || LevelOne.Contains(stream)))
                return;

            Logger.Write(LogLevel.Debug, () => $"Caching stream [{stream}]");
            var future = new DateTimeOffset().AddMinutes(1);
            MemCache.Set(stream, cached, new CacheItemPolicy { AbsoluteExpiration = future });
        }
        public void Evict(string stream)
        {
            Logger.Write(LogLevel.Debug, () => $"Evicting stream [{stream}] from cache");
            if (_intelligent)
            {
                if (Uncachable.Contains(stream)) return;

                if (LevelZero.Contains(stream))
                {
                    if (LevelOne.Contains(stream))
                    {
                        Logger.Write(LogLevel.Info, () => $"Stream [{stream}] has been evicted frequenty, marking uncachable for a few minutes");
                        Uncachable.Add(stream);
                    }
                    else
                        LevelOne.Add(stream);
                }
                else
                    LevelZero.Add(stream);

            }
            MemCache.Remove(stream);
        }
        public object Retreive(string stream)
        {
            var cached = MemCache.Get(stream);

            return cached;
        }

        public bool Update(string stream, object payload)
        {
            var cached = MemCache.Get(stream);

            if (!(cached is IEventStream) || !(payload is IWritableEvent)) return false;

            Logger.DebugFormat("Updating cached stream [{0}]", stream);
            var real = (cached as IEventStream);
{'fraction_non_alphanumeric': 0.090882945067587, 'fraction_numerical': 0.004889272361230946, 'mean_word_length': 1.930889169827223, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4888894', 'n_tokens_mistral': 1769, 'n_tokens_neox': 1666, 'n_words': 472}