Here is my code:
public class ConfigCache
{
private static volatile ObjectCache _cache = MemoryCache.Default;
private const string KeyModule = "MODULE_XDOC_KEY";
private static string _settingFile;
public ConfigCache(string file)
{
_settingFile = file;
}
public XDocument Get()
{
var doc = _cache[KeyModule] as XDocument;
if (doc == null)
{
doc = XDocument.Load(_settingFile);
var policy = new CacheItemPolicy();
var filePaths = new List<string> {_settingFile};
policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths));
var callback = new CacheEntryRemovedCallback(this.MyCachedItemRemovedCallback);
policy.RemovedCallback = callback;
_cache.Set(KeyModule, doc, policy);
}
return _cache[KeyModule] as XDocument;
}
private void MyCachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
{
// Log these values from arguments list
}
}
When run into _cache.Set() first time, it works fine:
_cache.Set() works well, it add the xdoc into cache.
But after several minutes(1 or 2 minutes), cache will not work anymore:
_cache.Set() does not insert anything into cache
_cache.Set() does not report any error.
the callback MyCachedItemRemovedCallback never triggered.
Someone met same issue:
MemoryCache always returns "null" after first expiration
But it seems not resolved yet. Anyone have any idea on this?
Your problem might be that the cache is being disposed. This will result in the MemoryCache silently returning null rather than throwing any exception. First search your code to be sure you are not disposing it. If you are sure you aren't disposing it, try breaking on the AppDomain.UnhandledException event. MemoryCache subscribes to this event (see this answer and disposes itself. If you are handling the UnhandledException event in your app as a global error handler, this could be the problem.
If this is the issue, a simple workaround is to handle the event and recreate a new instance of the cache. (See my answer here)
Related
I am using MemoryCache in one of my project to cache some keys and value. I have attached a listener on my MemoryCache that whenever it is expiring any items then that listener should get called so that I can remove those keys from CacheKeys HashSet as well. Basically I want to be consistent with what I have in CacheKeys and MemoryCache.
I read about MemoryCache and looks like I can use RegisterPostEvictionCallback as shown below:
private static readonly HashSet<string> CacheKeys = new HashSet<string>();
private bool CacheEntries<T>(MemoryCache memoryCache, string cacheKey, T value, Configuration config, Action<MemoryCacheEntryOptions> otherOptions = null)
{
int minutes = randomGenerator.Next(config.LowTime, config.HighTime);
MemoryCacheEntryOptions options = new MemoryCacheEntryOptions()
{
Size = config.Size,
Priority = config.Priority,
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(minutes)
};
// attaching listener
options.RegisterPostEvictionCallback(callback: EvictionCallback, state: this);
if (otherOptions != null) otherOptions(options);
CacheKeys.Add(cacheKey);
memoryCache.Set<T>(cacheKey, value, options);
return true;
}
private void EvictionCallback(object key, object value, EvictionReason reason, object state)
{
CacheKeys.Remove(key);
var message = $"Entry was evicted. Reason: {reason}.";
Console.WriteLine(message);
}
It looks like there is some issue where items doesn't expire automatically as per this this thread.
So to avoid the issue mentioned in that thread. Do I need to remove AbsoluteExpirationRelativeToNow and use CancellationTokenSource here?
If yes, how should I go ahead and make the change since I don't want to change the current functionality that I have already in my original code so if I remove AbsoluteExpirationRelativeToNow and use CancellationTokenSource here will it behave differently than what my original code is doing?
A Java guy is trying to learn some C# by diving into some existing C# code, checking user credentials with PowerShell.
The code below which I am trying to understand is in the context of possible performance problems.
The method below, CheckCredentials(), is called quite often and seem to be stuck for long periods of time, 60 seconds and more. My first suspect, of course, is the lock() part. I fail to understand why the lock is needed, if it is.
According to the docs, InitialSessionState.CreateDefault()and RunspaceFactory.CreateRunspace() are thread safe as they are public static methods (relevant sections here and here)
All objects used in the method are created anew; the only shared variables are _lock (and I am not sure why it is there at all) and modulePath. So, essentially, only the modulePath can be accessed by multiple threads. But, that doesn't seem like a problem. It's only a path, and the code is importing a PS module.
My question is: does the code below need to be "locked" at all?
private readonly object _lock = new object();
private readonly string modulePath;
public async Task<bool> CheckCredentials(string username, string password)
{
try
{
// Create Initial Session State for runspace.
var initialSession = InitialSessionState.CreateDefault();
initialSession.ImportPSModule(new[] { modulePath });
// Create credential object.
var credential = GetPowerShellCredential(username, password);
// Create command to connect office 365.
var connectCommand = new Command("Connect-MsolService");
connectCommand.Parameters.Add((new CommandParameter("Credential", credential)));
lock(_lock)
{
using (var psRunSpace = RunspaceFactory.CreateRunspace(initialSession))
{
psRunSpace.Open();
psRunSpace.Invoke(connectCommand);
}
return true;
};
}
catch (Exception e)
{
log.Info("Failed to check credentials.", e);
return false;
}
}
I have a slow and expensive method that return some data for me:
public Data GetData(){...}
I don't want to wait until this method will execute. Rather than I want to return a cached data immediately.
I have a class CachedData that contains one property Data cachedData.
So I want to create another method public CachedData GetCachedData() that will initiate a new task(call GetData inside of it) and immediately return cached data and after task will finish we will update the cache.
I need to have thread safe GetCachedData() because I will have multiple request that will call this method.
I will have a light ping "is there anything change?" each minute and if it will return true (cachedData != currentData) then I will call GetCachedData().
I'm new in C#. Please, help me to implement this method.
I'm using .net framework 4.5.2
The basic idea is clear:
You have a Data property which is wrapper around an expensive function call.
In order to have some response immediately the property holds a cached value and performs updating in the background.
No need for an event when the updater is done because you poll, for now.
That seems like a straight-forward design. At some point you may want to use events, but that can be added later.
Depending on the circumstances it may be necessary to make access to the property thread-safe. I think that if the Data cache is a simple reference and no other data is updated together with it, a lock is not necessary, but you may want to declare the reference volatile so that the reading thread does not rely on a stale cached (ha!) version. This post seems to have good links which discuss the issues.
If you will not call GetCachedData at the same time, you may not use lock. If data is null (for sure first run) we will wait long method to finish its work.
public class SlowClass
{
private static object _lock;
private static Data _cachedData;
public SlowClass()
{
_lock = new object();
}
public void GetCachedData()
{
var task = new Task(DoStuffLongRun);
task.Start();
if (_cachedData == null)
task.Wait();
}
public Data GetData()
{
if (_cachedData == null)
GetCachedData();
return _cachedData;
}
private void DoStuffLongRun()
{
lock (_lock)
{
Console.WriteLine("Locked Entered");
Thread.Sleep(5000);//Do Long Stuff
_cachedData = new Data();
}
}
}
I have tested on console application.
static void Main(string[] args)
{
var mySlow = new SlowClass();
var mySlow2 = new SlowClass();
mySlow.GetCachedData();
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
mySlow.GetData();
mySlow2.GetData();
}
mySlow.GetCachedData();
Console.Read();
}
Maybe you can use the MemoryCache class,
as explained here in MSDN
So I encountered a strange issue today - I had a simple creation of an instance inside the critical section of a lock, and it would throw a null reference exception when I manually dragged the next line to execute. To illustrate:
public class SearchEngineOptimizationParser
{
protected static ConcurrentDictionary<string, SearchEngineOptimizationInfo> _referralInformation = null;
protected static DateTime _lastRecordingDate;
protected static object _lockRecordingObject = new object();
protected static Dictionary<string, string> _searchProviderLookups = null;
static SearchEngineOptimizationParser()
{
_referralInformation = new ConcurrentDictionary<string, SearchEngineOptimizationInfo>();
_lastRecordingDate = DateTime.Now;
_searchProviderLookups = new Dictionary<string, string>();
_searchProviderLookups.Add("google.com", "q");
_searchProviderLookups.Add("yahoo.com", "p");
_searchProviderLookups.Add("bing.com", "q");
}
public SearchEngineOptimizationParser()
{
}
public virtual void ParseReferrer(Uri requestUrl, NameValueCollection serverVariables, ISession session)
{
string corePath = requestUrl.PathAndQuery.SmartSplit('?')[0].ToLower();
string referrer = serverVariables["HTTP_REFERER"];
if (!string.IsNullOrWhiteSpace(referrer))
{
NameValueCollection queryString = HttpUtility.ParseQueryString(referrer);
string dictionaryKey = session.AffiliateID + "|" + corePath;
foreach (var searchProvider in _searchProviderLookups)
{
if (referrer.Contains(searchProvider.Key))
{
if (queryString[searchProvider.Value] != null)
{
string keywords = queryString[searchProvider.Value];
SearchEngineOptimizationInfo info = new SearchEngineOptimizationInfo
{
Count = 1,
CorePath = corePath,
AffiliateId = session.AffiliateID,
Keywords = keywords
};
_referralInformation.AddOrUpdate(dictionaryKey, info, (key, oldValue) =>
{
oldValue.Count++;
return oldValue;
});
break;
}
}
}
}
if (DateTime.Now > _lastRecordingDate.AddHours(1))
{
lock (_lockRecordingObject)
{
if (DateTime.Now > _lastRecordingDate.AddHours(1))
{
SearchEngineKeywordRepository repository = new SearchEngineKeywordRepository();
List<KeyValuePair<string, SearchEngineOptimizationInfo>> currentInfo = _referralInformation.ToList();
Action logData = () =>
{
foreach (var item in currentInfo)
repository.LogKeyword(item.Value);
};
Thread logThread = new Thread(new ThreadStart(logData));
logThread.Start();
_lastRecordingDate = DateTime.Now;
_referralInformation.Clear();
}
}
}
}
EDIT: Updated Real Object
public class SearchEngineKeywordRepository
{
public virtual void LogKeyword(SearchEngineOptimizationInfo keywordInfo)
{
LogSearchEngineKeywords procedure = new LogSearchEngineKeywords();
procedure.Execute(keywordInfo.CorePath, keywordInfo.AffiliateId, keywordInfo.Keywords, keywordInfo.Count);
}
}
The general pattern being that I want to do this 'something' only every hour (in the context of a website application that gets a lot of traffic). I would breakpoint my first if statement, and then step the next line to execute inside the second if statement. When doing so, the act of initializing the SomeObject instance would cause a null reference exception. It had a completely 100% default constructor - I didn't even specify one.
However, when I let the code go through naturally, it would execute without problem. For some reason, it seems that when I skipped over the lock call into the critical section to just test run that code, it caused all kinds of errors.
I'm curious to know why that is; I understand the lock keyword is just syntactic sugar for a Monitor.Enter(o) try / finally block, but that seems to be that when invoking the constructor, something else was happening.
Anyone have any ideas?
EDIT: I've added the actual code to this. I'm able to reproduce this at will, but I still don't understand why this is happening. I've tried copying this code to another solution and the problem does not seem to occur.
I've tried to reproduce your situation, but as I expected I could not. I've tried both the 2.0 and 4.0 runtime, in 32 and 64 bit mode (debugging sometimes behaves differently under x64).
Is the code shown a simplification? Have you checked all your assumptions? I understand you're skipping 3 lines of code, both the if statements and the lock? In that case, even setting the lock object to null does not cause the exception you describe.
(Having _lockRecordingObject set to null causes an ArgumentNullException when leaving the lock scope)
I have written my own custom change monitor class for the .NET MemoryCache. It seems to initialize fine, but when I attempt to add it to the Cache, it throws an InvalidOperation exception - The method has already been invoked, and can only be invoked once.
My change monitor class:
internal class MyChangeMonitor : ChangeMonitor
{
private Timer _timer;
private readonly string _uniqueId;
private readonly TypeAsOf _typeAsOf;
private readonly string _tableName;
public GprsChangeMonitor(TypeAsOf typeAsOf, string tableName)
{
bool initComplete = false;
try
{
_typeAsOf = typeAsOf;
_tableName = tableName;
_uniqueId = Guid.NewGuid().ToString();
TimeSpan ts = new TimeSpan(0, 0, 5, 0, 0);
_timer = new Timer {Interval = ts.TotalMilliseconds};
_timer.Elapsed += CheckForChanges;
_timer.Enabled = true;
_timer.Start();
initComplete = true;
}
finally
{
base.InitializationComplete();
if(!initComplete)
Dispose(true);
}
}
void CheckForChanges(object sender, System.Timers.ElapsedEventArgs e)
{
//check for changes, if different
base.OnChanged(_typeAsOf);
}
}
The code I use to create the cache policy and add the key/value pair to the cache:
CacheItemPolicy policy = new CacheItemPolicy
{
UpdateCallback = OnCacheEntryUpdateCallback
};
policy.AbsoluteExpiration = SystemTime.Today.AddHours(24);
//monitor the for changes
string tableName = QuickRefreshItems[type];
MyChangeMonitor cm = new MyChangeMonitor(typeAsOf, tableName);
policy.ChangeMonitors.Add(cm);
cm.NotifyOnChanged(OnRefreshQuickLoadCacheItems);
MyCache.Set(cacheKey, value, policy);
The Set call throws the invalid operation exception which is weird because, according to the MSDN documentation, it only throws the ArgumentNull, Argument, ArgumentOutOfRange, and NotSupported exceptions.
I am sure that I must be making a simple mistake. But it's hard to find good documentation or examples on writing your own custom change monitor. Any help would be appreciated.
I know the comments have the answer, but I wanted it to be more obvious...
When a ChangeMonitor is used, it will fire immediately if the cache entry does not exist.
MSDN documentation states it this way:
A monitored entry is considered to have changed for any of the
following reasons:
A) The key does not exist at the time of the call to the
CreateCacheEntryChangeMonitor method. In that case, the resulting
CacheEntryChangeMonitor instance is immediately set to a changed
state. This means that when code subsequently binds a
change-notification callback, the callback is triggered immediately.
B) The associated cache entry was removed from the cache. This can
occur if the entry is explicitly removed, if it expires, or if it is
evicted to recover memory
I've had the exact same error:
Source: System.Runtime.Caching
Exception type: System.InvalidOperationException
Message: The method has already been invoked, and can only be invoked once.
Stacktrace: at System.Runtime.Caching.ChangeMonitor.NotifyOnChanged(OnChangedCallback onChangedCallback)
at System.Runtime.Caching.MemoryCacheEntry.CallNotifyOnChanged()
at System.Runtime.Caching.MemoryCacheStore.AddToCache(MemoryCacheEntry entry)
at System.Runtime.Caching.MemoryCacheStore.Set(MemoryCacheKey key, MemoryCacheEntry entry)
at System.Runtime.Caching.MemoryCache.Set(String key, Object value, CacheItemPolicy policy, String regionName)
I've searched for it for hours.. until the light of logic struck me:
I was using a static policy object that was reused.. (some unconscious process in me reuses all objects if they are equal,maybe I am afraid of constructing objects that consume some bytes in memory )
By creating a new policy object for every item in the cache, the error was gone. Pretty logical if you think about it.
Posting a late answer as I've just faced the same issue and conducted my own investigation.
When you register your change monitor with a cached item policy — policy.ChangeMonitors.Add(cm) — the CacheItemPolicy implementation registers its own change callback on it via ChangeMonitor.NotifyOnChanged. You're not supposed to be calling cm.NotifyOnChanged to register yet another callback, or it will throw The method has already been invoked, and can only be invoked once at that point.
Instead, use CacheItemPolicy.UpdateCallback or CacheItemPolicy.RemovedCallback to update/remove the cache item, e.g. as described in this blog post.