1

我正在使用 NServiceBus 构建一个系统,而我的 DataLayer 正在使用 Linq 2 SQL。

该系统由 2 个服务组成。

Service1 从 NSB 接收消息。它将在我的数据库中查询 Table1 并将记录插入 Table1 如果满足某个条件,则将新的 NSB 消息发送到第二个服务

当 Service2 接收到来自 Service1 的消息并执行其他一些与数据库无关的工作时,它也会更新 Table1 中的记录。Service2 是一个长时间运行的进程。

我遇到的问题是 Service2 更新 Table1 中的记录时,该表被锁定。在 Service2 完成所有处理之前,锁似乎就位。即我的数据上下文被释放后,锁没有被释放。

这会导致 Service1 中的查询超时。一旦 Service2 完成处理,Service1 将再次恢复处理而不会出现问题。

因此,例如 Service1 代码可能如下所示:

int x =0;
using (DataContext db = new DataContext())
{
  x = (from dp in db.Table1 select dp).Count(); // this line will timeout while service2 is processing

  Table1 t = new Table1();
  t.Data = "test";
  db.Table1.InsertOnSubmit(t);
  db.SubmitChanges();
}

if(x % 50 == 0)
  CallService2();

service2 中的代码可能如下所示:

using (DataContext db = new DataContext())
{
  Table1 t = db.Table1.Where(t => t.id == myId);
  t.Data = "updated";

  db.SubmitChanges();

}

// I would have expected the lock to have been released at this point, but this is not the case.

DoSomeLongRunningTasks();

// lock will be released once service2 exits

我不明白为什么在Service2中处理datacontext时没有释放锁。

为了解决我一直在打电话的问题:

db.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");

这行得通,但我不喜欢使用它。我想正确解决这个问题。

以前有没有人遇到过这种问题,有人知道如何解决吗?为什么数据上下文被释放后锁没有被释放?

提前致谢。

ps 很抱歉这篇文章太长了。

编辑:

在现实世界(ish)的情况下看这个,比如运输:

service1 将 crate 记录添加到数据库中。如果存在特定数量或板条箱记录,则将板条箱添加到容器记录(如果不存在容器记录,则创建一个),然后创建运输记录并关闭所有容器并分配给运输记录。

然后调用 service2 处理运输记录。对 service2 的调用是 Bus.Send 调用,但可能是传奇的一部分。service2 将更新每个 crate 记录,它被分配到哪个船。然后处理一些其他的运输指令。当 service2 正在处理时,可能会收到更多的 crate 用于下一次运输,但就目前而言,在 service2 完成运输处理之前,它们不能分配到容器中。

4

1 回答 1

4

您看到这种行为的原因是 NServiceBus 使用的默认隔离级别(与 TransactionScope 相同)是 Serializable - 它锁定了整个表。

您要做的是在 NServiceBus 级别设置不同的隔离级别。

为此,您需要使用流畅的初始化 API,并在调用 .MsmqTransport() 之后调用方法 .IsolationLevel(IsolationLevel.ReadCommitted) 或传入其他值。我不建议低于这个值(喜欢阅读未提交的内容)。

在上述场景中,我不会将所有这些都放在一个处理程序中,而是更喜欢使用 saga 来管理顶级流程,因为您似乎很高兴在处理程序的中途打破隔离边界。

希望有帮助。

于 2010-04-23T18:10:18.080 回答