[英].net6.0 - write forward - AWS Aurora: The MySQL server is running with the --read-only option so it cannot execute this statement
我们的 AWS RDS (Aurora MySQL 2.10.2) 有一个主集群和一个辅助集群。
辅助集群是一个副本并有一个读取器实例。 此读取器实例设置为 read_only false ,并且我已设置从辅助集群到主集群的写入转发。 因此,我将参数组的变量init_connect
设置为值set @@aurora_replica_read_consistency=SESSION
当我通过 MySQL Workbench 连接到辅助集群的读取端点时,写入转发工作,即如果我发出查询以在辅助集群中创建记录,它会写入主集群并查询同一个表显示新记录。
但是,问题出现在我的 .net6.0 应用程序中。 如果我将使用Pomelo.EntityFrameworkCore.MySql的 .net6.0 应用程序指向 connectionstring 中的同一个辅助集群的读取器端点,则它不起作用并且失败并出现此错误:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
---> MySqlConnector.MySqlException (0x80004005): The MySQL server is running with the --read-only option so it cannot execute this statement
at MySqlConnector.Core.ServerSession.ReceiveReplyAsyncAwaited(ValueTask`1 task) in /_/src/MySqlConnector/Core/ServerSession.cs:line 954
at MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in /_/src/MySqlConnector/Core/ResultSet.cs:line 44
at MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 127
at MySqlConnector.MySqlDataReader.CreateAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary`2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 456
at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(IReadOnlyList`1 commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 56
at MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 330
at MySqlConnector.MySqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 323
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass33_0`2.<<ExecuteAsync>b__0>d.MoveNext()
由于辅助集群init_connect
上的参数组变量设置为值@@aurora_replica_read_consistency=SESSION
,它在技术上应该有效。
当我在辅助集群上查找 ready_only 变量详细信息时,我看到它被设置为 false:
show global variables like 'read_only%';
Variable_Name Value
read_only OFF
有谁知道我是否在这里遗漏了什么?
更新
在我的 .net6.0 应用程序中,如果我在 SaveChanges() 之前显式执行set @@aurora_replica_read_consistency=SESSION
,我会得到一个不同的错误。 这个并发错误似乎具有误导性,因为我是唯一一个连接到辅助集群的人。
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithoutPropagationAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeAsync(RelationalDataReader reader, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass33_0`2.<<ExecuteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
嗅探来自应用程序的 MySQL 流量,我注意到此 SaveChanges() 与select ROW_COUNT();
. 当 ROW_COUNT() 执行时,它返回 0 导致上述异常,然后事务回滚。
首先使用无法更改记录的DbContext
检查应用程序中的变量值:
var connection = context.Database.GetDbConnection();
if (connection.State != ConnectionState.Open)
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "select @@read_only;";
var result = (long)command.ExecuteScalar();
Trace.Assert(result == 0);
您还想检查打开的连接使用的连接字符串,并确保它是预期的。
评论您的结果或更新您的问题,我们将从那里获取。
@soccer7 的解决方案为我解决了这个问题
您需要实现一个DbCommandInterceptor
,拆分 EF 的查询,执行查询和SuppressFinalize
SELECT ROW_COUNT();
这是我的解决方案:
public class ForwardWritesCommandInterceptor : DbCommandInterceptor
{
private const string UPDATE_COMMAND = "SELECT ROW_COUNT();";
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
if (ShouldSplitQuery(command.CommandText))
{
return SplitUpdateQueryAsync(command).Result;
}
return base.ReaderExecuting(command, eventData, result);
}
public override async ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
{
if (ShouldSplitQuery(command.CommandText))
{
return await SplitUpdateQueryAsync(command);
}
return await base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
private bool ShouldSplitQuery(string query) => query.Trim().EndsWith(UPDATE_COMMAND);
/// <summary>
/// Execute the UPDATE part of the query, then SuppressWithResult the SELECT part
/// </summary>
private async ValueTask<InterceptionResult<DbDataReader>> SplitUpdateQueryAsync(DbCommand command)
{
command.CommandText = command.CommandText.Substring(0, command.CommandText.LastIndexOf(UPDATE_COMMAND));
await command.ExecuteNonQueryAsync();
command.CommandText = UPDATE_COMMAND;
return InterceptionResult<DbDataReader>.SuppressWithResult(await command.ExecuteReaderAsync());
}
}
在services.AddDbContext
中注册:
services.AddDbContext<FooContext>(options =>
...
options.AddInterceptors(new ForwardWritesCommandInterceptor());
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.