Мой сервер использует MySqlConnector и взаимодействует с базой данных MySQL на AWS. Я храню 5-минутные порции счетчиков API в MySQL. Эти счетчики API увеличиваются с каждым вызовом API и обрабатываются кодом ConcurrentDictionary (что не является проблемой). Эта строка кода недавно вызвала исключение, которое является частью запроса linq через MySqlConnector для доступа к таблице базы данных MySQL:
await _context.ApiCounts.Where(c => c.ApiName == apiName && c.StartTime >= startTime).ToListAsync();
Я никогда не видел этого раньше произошел сбой линии, но внезапно один из моих серверов начал выдавать следующее исключение в строке выше:
InvalidOperationException: 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.
at System.ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported
at System.Collections.Generic.Dictionary`2.FindEntry
at System.Collections.Generic.Dictionary`2.TryGetValue
at Remotion.Linq.Parsing.Structure.NodeTypeProviders.MethodInfoBasedNodeTypeRegistry.GetNodeType
at Remotion.Linq.Parsing.Structure.NodeTypeProviders.MethodInfoBasedNodeTypeRegistry.IsRegistered
at System.Linq.Enumerable.Any
at Remotion.Linq.Parsing.Structure.ExpressionTreeParser.GetQueryOperatorExpression
at Remotion.Linq.Parsing.ExpressionVisitors.SubQueryFindingExpressionVisitor.Visit
at System.Linq.Expressions.ExpressionVisitor.VisitBinary
at System.Linq.Expressions.BinaryExpression.Accept
at System.Linq.Expressions.ExpressionVisitor.VisitBinary
at System.Linq.Expressions.BinaryExpression.Accept
at System.Linq.Expressions.ExpressionVisitor.VisitLambda
at System.Linq.Expressions.Expression`1.Accept
at System.Linq.Enumerable+SelectListPartitionIterator`2.ToArray
at System.Linq.Enumerable.ToArray
at Remotion.Linq.Parsing.Structure.MethodCallExpressionParser.Parse
at Remotion.Linq.Parsing.Structure.ExpressionTreeParser.ParseMethodCallExpression
at Remotion.Linq.Parsing.Structure.QueryParser.GetParsedQuery
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileAsyncQueryCore
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>c__DisplayClass24_0`1.<CompileAsyncQuery>b__0
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator
at System.Linq.AsyncEnumerable+<Aggregate_>d__6`3.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
at BlayFap.Controllers.ServerController+<GetCachedApiCount>d__13.MoveNext (E:\Projects\BlayFap\BlayFap\Controllers\ServerController.cs:342)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
at BlayFap.Controllers.ServerController+<GetApiCount>d__14.MoveNext (E:\Projects\BlayFap\BlayFap\Controllers\ServerController.cs:378)
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeActionMethodAsync>d__12.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeNextActionFilterAsync>d__10.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeInnerFilterAsync>d__14.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResourceFilter>d__22.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeFilterPipelineAsync>d__17.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeAsync>d__15.MoveNext
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw
Доступ ко всем остальным таблицам возможен нормально, но с этого момента при любой попытке использовать таблицу ApiCounts результаты в вышеупомянутом исключении. Я должен был на самом деле перезагрузить уязвимый сервер, чтобы очистить ошибку. Похоже, что это исключение связано с одновременным редактированием словаря, и этот словарь представлен в коде linq. Я предполагаю, что сервер, возможно, попал в ситуацию, когда EF одновременно обновлял и записывал какие-то данные во время выполнения оператора linq, а затем застрял словарь в странном состоянии, но я не уверен, как защититься от этого и где настоящая проблема. Это ошибка. NET или моя?
Подробнее: я не изменяю результат из ApiCounts в этой функции и не вызываю SaveChanges. Однако для кода, выполняющего asyn c с этим (в другом REST-запросе), возможно обновить ApiCounts и вызвать SaveChanges.