繁体   English   中英

.net6.0 - 向前写入 - AWS Aurora:MySQL 服务器正在使用 --read-only 选项运行,因此它无法执行此语句

[英].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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM