并发 API 个请求 - 数据库冲突

Concurrent API requests - Database Conflicts

我对(任何)一个 API 端点(ASP.NET Web API 2)的并发请求有问题。当第二个请求在第一个请求完成之前开始处理时,数据库实体可能会出现并发问题。

示例:
1:请求R1开始被处理,它将实体E1和E2读入内存
2:请求R2开始被处理,它将实体E2和E1读入内存
3:请求R1更新对象E1
4:请求R1创建E3,如果E1和E2已经更新
5:请求R2更新对象E2
6:请求R2创建E3,如果E1和E2已经更新

问题是两个请求都在另一个请求更新实体 E1 和 E2 之前读取了它。所以两者都看到过时的、未更新的版本,并且永远不会创建 E3。

所有请求都在单个数据库事务中处理(SQL Server 2017 上的隔离级别 "read committed"、Entity Framework 6)。 在我目前的理解中,我不认为这个问题可以在数据 access/data 存储级别(例如更严格的数据库隔离级别或乐观锁定)上解决,但需要在代码层次结构(请求级别)中更高。

我的建议是悲观地锁定所有在进入 API 方法时可以更新的实体。当另一个 API 请求开始时需要对任何已经锁定的实体进行写锁定,它需要等待直到所有先前的锁定都被释放(队列)。一个请求通常在不到 250 毫秒内处理。
这可以通过装饰 API 方法的自定义属性来实现:[LockEntity(typeof(ExampleEntity), exampleEntityId)]。单个 API 方法可以用零到多个这样的属性来修饰。如果可能, 锁定机制 将与 async/await 一起工作,或者只是让线程休眠。 锁同步需要跨多个服务器工作,所以我在这里看到的最简单的选择是在应用程序数据存储(单个,全局)中的表示。
这种方法很复杂并且有一些性能缺点(阻塞请求处理,在锁定处理期间执行数据库查询)所以我愿意接受任何建议或其他想法。

我的问题:
1.有没有描述这个问题的术语?这似乎是一件很常见的事情,但到目前为止我还不知道。
2. 是否有解决此问题的最佳实践?
3. 如果没有,我的提议有意义吗?

我不会绕过无状态的网络和 REST,也不会阻止请求。 250 毫秒是四分之一秒,IMO 非常慢。例如 100 个并发请求将相互阻塞,并且至少一个流将等待 25 秒!这会大大降低应用程序的速度。

我见过一些应用程序(包括 Atlassians Confluence)会通知用户当前数据集同时已被另一个用户更改。 (有时也会提到其他用户。)我会使用 Websockets 以同样的方式进行操作,例如信号R。 service-now 等其他应用程序正在将远程更改合并到当前打开的数据集中。

如果您不想使用 Websockets,您也可以尝试在服务器端进行合并或检查,并使用特定的 HTTP 状态代码和消息通知用户。告诉用户这期间发生了什么变化,尝试合并并询问合并是否正常。