We recently went to a CSVFS clustered file server. I have a file watcher that's monitoring 4 directories for OnCreated
and OnRenamed
events but whenever the node changes, it causes a buffer overflow with the error
Too many changes at once in directory
The the watchers are automatically restarted and the process continues to work but begins writing errors when the OnCreated
/OnRenamed
events are fired.
Cannot access a disposed object.
Object name: 'FileSystemWatcher'.
at System.IO.FileSystemWatcher.StartRaisingEvents()
at System.IO.FileSystemWatcher.set_EnableRaisingEvents(Boolean value)
In the OnCreated
method below, if I was to do this, should it work?
watchit = source as FileSystemWatcher;
I don't actually assign the newly created FileSystemWatcher
to watchit
anywhere else.
More details/code
The watchers are created via a foreach loop when the process initially starts. FileChange
is simply a method that determines the type of change, does a bit of work, then triggers the correct action for the change type.
foreach (string subDir in subDirs)
{
string localFolder = $"{subDir}";
watchit = new FileSystemWatcher
{
Path = localFolder,
EnableRaisingEvents = true,
IncludeSubdirectories = false,
NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime,
Filter = watchFor,
InternalBufferSize = 65536,
SynchronizingObject = null //,
};
watchit.Changed += FileChange;
watchit.Created += FileChange;
watchit.Deleted += FileChange;
watchit.Renamed += OnRename;
watchit.Error += OnError;
watchit.EnableRaisingEvents = true;
watchers.Add(watchit);
Console.WriteLine($"watching {subDir} for {watchFor}");
}
watchit
is a static FileSystemWatcher
set globally.
private static async Task<int> OnCreated<T>(object source, FileSystemEventArgs e, string ext)
{
int insertResult = 0;
try
{
watchit.EnableRaisingEvents = false;
EventLogWriter.WriteEntry("File: " + e.FullPath + " " + e.ChangeType);
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType + " " + DateTime.Now);
insertResult = await FileHandler.FileHandlers().ConfigureAwait(false);
watchit.EnableRaisingEvents = true;
// if (insertResult > 0) File.Delete(e.FullPath);
}
catch (Exception ex)
{
Logger.Trace($"{ex.Message} {ex.StackTrace} {ex.InnerException}");
EventLogWriter.WriteEntry($"{ex.Message} {ex.StackTrace} {ex.InnerException}",
EventLogEntryType.Error);
watchit.EnableRaisingEvents = true;
}
finally
{
watchit.EnableRaisingEvents = true;
}
return insertResult;
}
These are my error handling methods.
private static void OnError(object source, ErrorEventArgs e)
{
if (e.GetException().GetType() == typeof(InternalBufferOverflowException))
{
EventLogWriter.WriteEntry($"Error: File System Watcher internal buffer overflow at {DateTime.Now}", EventLogEntryType.Warning);
}
else
{
EventLogWriter.WriteEntry($"Error: Watched directory not accessible at {DateTime.Now}", EventLogEntryType.Warning);
}
MailSend.SendUploadEmail($"ASSIST NOTES: {e.GetException().Message}", "The notes service had a failure and should be restarted.", "admins", e.GetException(), MailPriority.High);
NotAccessibleError(source as FileSystemWatcher, e);
}
/// <summary>
/// triggered on accessible error.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="e">The <see cref="ErrorEventArgs"/> instance containing the event data.</param>
private static void NotAccessibleError(FileSystemWatcher source, ErrorEventArgs e)
{
EventLogWriter.WriteEntry($"Not Accessible issue. {e.GetException().Message}" + DateTime.Now.ToString("HH:mm:ss"));
int iMaxAttempts = 120;
int iTimeOut = 30000;
int i = 0;
string watchPath = source.Path;
string watchFilter = source.Filter;
int dirExists = 0;
try
{
dirExists = Directory.GetFiles(watchPath).Length;
}
catch (Exception) { }
try
{
while (dirExists == 0 && i < iMaxAttempts)
{
i += 1;
try
{
source.EnableRaisingEvents = false;
if (!Directory.Exists(source.Path))
{
EventLogWriter.WriteEntry(
"Directory Inaccessible " + source.Path + " at " +
DateTime.Now.ToString("HH:mm:ss"));
Console.WriteLine(
"Directory Inaccessible " + source.Path + " at " +
DateTime.Now.ToString("HH:mm:ss"));
System.Threading.Thread.Sleep(iTimeOut);
}
else
{
// ReInitialize the Component
source.Dispose();
source = null;
source = new System.IO.FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(source)).BeginInit();
source.EnableRaisingEvents = true;
source.Filter = watchFilter;
source.Path = watchPath;
source.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime;
source.Created += FileChange;
source.Renamed += OnRename;
source.Error += new ErrorEventHandler(OnError);
((System.ComponentModel.ISupportInitialize)(source)).EndInit();
EventLogWriter.WriteEntry(
$"Restarting watcher {watchPath} at " + DateTime.Now.ToString("HH:mm:ss"));
dirExists = 1;
}
}
catch (Exception error)
{
EventLogWriter.WriteEntry($"Error trying Restart Service {watchPath} " + error.StackTrace +
" at " + DateTime.Now.ToString("HH:mm:ss"));
source.EnableRaisingEvents = false;
System.Threading.Thread.Sleep(iTimeOut);
}
}
//Starts a new version of this console appp if retries exceeded
//Exits current process
var runTime = DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime();
if (i >= 120 && runTime > TimeSpan.Parse("0:00:30"))
{
Process.Start(Assembly.GetExecutingAssembly().Location);
Environment.Exit(666);
}
}
catch (Exception erw) { }
}