[英]ASP.NET Core middleware handling SQL serialization failure retries?
我目前正在尝试编写一个 ASP.NET Core API 中间件,该中间件在执行底层 MVC 操作之前打开一个 SQL 事务。 该事务使用 Serializable 隔离级别,并由底层 MVC 操作中的所有 SQL 请求使用。 然后,当 MVC 操作退出时:
我最终得到的是:
public async Task InvokeAsync(HttpContext context, IDatabaseDriver databaseDriver)
{
context.Request.EnableBuffering(REQUEST_BUFFER_THRESHOLD);
int attempt = 0;
while (true)
{
attempt++;
try
{
await this._next(context);
await databaseDriver.Commit();
break;
}
catch (PostgresException ex)
when (ex.SqlState == PostgresErrorCodes.SerializationFailure &&
attempt <= MAX_RETRIES)
{
// SQL serialization failure: rollback and retry
await databaseDriver.Rollback();
context.Request.Body.Seek(0, SeekOrigin.Begin);
}
catch
{
// Unhandled error: rollback and throw
await databaseDriver.Rollback();
throw;
}
}
}
不幸的是,这不能正常工作,因为 SQL 序列化异常有时发生在await databaseDriver.Commit()
步骤,该步骤在操作成功返回并开始写入 HTTP 响应流后执行。 这会导致响应正文中出现重复的 JSON 数据。
解决这个问题的最佳方法是什么?
我最终通过在每次重试之前重置响应流来解决这个问题。 这通常是不可能的,因为响应流不可搜索,但您可以在中间件运行时使用临时MemoryStream
替换响应流:
public async Task InvokeAsync(HttpContext context, IDatabaseDriver databaseDriver)
{
context.Request.EnableBuffering(REQUEST_BUFFER_THRESHOLD);
Stream originalResponseBodyStream = context.Response.Body;
using var buffer = new MemoryStream();
context.Response.Body = buffer;
try
{
int attempt = 0;
while (true)
{
attempt++;
try
{
// Process request then commit transaction
await this._next(context);
await databaseDriver.Commit();
break;
}
catch (PostgresException ex)
when (ex.SqlState == PostgresErrorCodes.SerializationFailure &&
attempt <= MAX_RETRIES)
{
// SQL serialization failure: rollback and retry
await databaseDriver.Rollback();
context.Request.Body.Seek(0, SeekOrigin.Begin);
context.Response.Body.SetLength(0);
}
catch
{
// Unhandled error: rollback and throw
await databaseDriver.Rollback();
throw;
}
}
}
finally
{
context.Response.Body = originalResponseBodyStream;
buffer.Seek(0, SeekOrigin.Begin);
await buffer.CopyToAsync(context.Response.Body);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.