StreamReader and file locks
I’ve been seeing errors raised by nLog over the past few days, and I couldn’t work out why. The errors were all along the lines of
2013-04-18 16:53:40.5494 Warn Error while writing to 'File Target[processlog_wrapped]': System.IO.FileLoadException: The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at NLog.Internal.FileAppenders.BaseFileAppender.WindowsCreateFile(String fileName, Boolean allowConcurrentWrite)
at NLog.Internal.FileAppenders.BaseFileAppender.TryCreateFileStream(Boolean allowConcurrentWrite)
at NLog.Internal.FileAppenders.BaseFileAppender.CreateFileStream(Boolean allowConcurrentWrite)
at NLog.Internal.FileAppenders.RetryingMultiProcessFileAppender.Write(Byte[] bytes)
at NLog.Targets.FileTarget.WriteToFile(String fileName, Byte[] bytes, Boolean justData)
at NLog.Targets.FileTarget.Write(LogEventInfo logEvent)
at NLog.Targets.Target.Write(AsyncLogEventInfo logEvent). Try 10/10
which is obviously delightful :)
The thing I couldn’t work out is what else was trying to access the file identified by processlog_wrapped. It only ever seemed to be the processlog (which in english is the log file that I log informational messages to, and which I use to give me usage stats) which was causing me problems. I initially wondered if it was related to the retryingwrapper changes but I just couldn’t get any idea of why. Finally, I wondered if it was my monitoring system. It reads the processlog file to get a count of how many orders have been processed in the day so far. It was using a StreamReader object, opening the file directly. I’d assumed (I know!) that as it was a reader, that it would open the file read only, and wouldn’t lock the file. Thanks to this stackoverflow question/answer I learnt that behind the scenes, however, it appears to open the file with a FileShare.Read, meaning that other processes can only open the file for reading and not for writing.
So, my new code looks like
using (var file = new FileStream(this.LogFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var reader = new StreamReader(file))
{
which is explicitly opening the FileStream so that other consumers can read and write to the file. It has only been deployed for a day or so, but I haven’t seen any of these errors since so I’m hopeful that this has at least helped the situation.