ElmahCore icon indicating copy to clipboard operation
ElmahCore copied to clipboard

Thread safety issue in ElmahLogFeature.

Open malylemire1 opened this issue 4 years ago • 2 comments

I randomly get this exception :

Exception : Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.

Details : at System.Collections.Generic.Dictionary2.FindValue(TKey key) at ElmahCore.ElmahLogFeature.SetSqlDuration(Guid id) at ElmahCore.Mvc.ElmahDiagnosticSqlObserver.OnNext(KeyValuePair2 value) at System.Diagnostics.DiagnosticListener.Write(String name, Object value) at System.Data.SqlClient.SqlClientDiagnosticListenerExtensions.WriteCommandAfter(DiagnosticListener this, Guid operationId, SqlCommand sqlCommand, String operation) at System.Data.SqlClient.SqlCommand.<>c__DisplayClass125_0.b__1(Task`1 t) at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)

Seems there is a thread safety issue with private readonly Dictionary<Guid, ElmahLogSqlEntry> _map = new Dictionary<Guid, ElmahLogSqlEntry>();

Consider using ConcurrentDictionary.

Thanks

malylemire1 avatar Sep 14 '21 14:09 malylemire1

Hi, this seems to happen when there is multiple threads using one DBContext on each thread. This problem occurs quite frequently so if you could check it out. A simple test sould allow you to reproduce the problem.

Thanks

malylemire1 avatar Jun 06 '22 12:06 malylemire1

Or could you add an option to disable the ElmahDiagnosticSqlObserver? Would be great with a simple overload of UseElmah

public static IApplicationBuilder UseElmah(this IApplicationBuilder app, bool enableSqlObserver)
        {
            app.UseStaticHttpContext();

            if (enableSqlObserver)
            {
                DiagnosticListener.AllListeners.Subscribe(new ElmahDiagnosticObserver(app.ApplicationServices));
            }

            app.UseMiddleware<ErrorLogMiddleware>();


            return app;
        }

malylemire1 avatar Jun 07 '22 19:06 malylemire1