如何在asp.net应用程序中实现“悲观锁定”?

时间:2022-10-11 06:51:20

I would like some advice from anyone experienced with implementing something like "pessimistic locking" in an asp.net application. This is the behavior I'm looking for:

我希望任何有经验的人在asp.net应用程序中实现类似“悲观锁定”的建议。这是我正在寻找的行为:

  1. User A opens order #313
  2. 用户A打开订单#313

  3. User B attempts to open order #313 but is told that User A has had the order opened exclusively for X minutes.
  4. 用户B尝试打开#313订单,但被告知用户A已经在X分钟内专门打开了订单。

Since I haven't implemented this functionality before, I have a few design questions:

由于我之前没有实现过这个功能,所以我有一些设计问题:

  • What data should i attach to the order record? I'm considering:
    • LockOwnedBy
    • LockAcquiredTime
    • LockRefreshedTime
  • 我应该将哪些数据附加到订单记录中?我正在考虑:LockOwnedBy LockAcquiredTime LockRefreshedTime

I would consider a record unlocked if the LockRefreshedTime < (Now - 10 min).

如果LockRefreshedTime <(现在 - 10分钟),我会考虑解锁记录。

  • How do I guarantee that locks aren't held for longer than necessary but don't expire unexpectedly either?
  • 我如何保证锁具的保存时间不超过必要但不会意外到期?

I'm pretty comfortable with jQuery so approaches which make use of client script are welcome. This would be an internal web application so I can be rather liberal with my use of bandwidth/cycles. I'm also wondering if "pessimistic locking" is an appropriate term for this concept.

我对jQuery很满意,所以欢迎使用客户端脚本的方法。这将是一个内部Web应用程序,所以我可以相当*地使用带宽/周期。我也想知道“悲观锁定”是否适合这个概念。

2 个解决方案

#1


It sounds like you are most of the way there. I don't think you really need LockRefreshedTime though, it doesn't really add anything. You may just as well use the LockAcquiredTime to decide when a lock has become stale.

听起来你大部分都在那里。我不认为你真的需要LockRefreshedTime,它并没有真正添加任何东西。你也可以使用LockAcquiredTime来决定锁何时变得陈旧。

The other thing you will want to do is make sure you make use of transactions. You need to wrap the checking and setting of the lock within a database transaction, so that you don't end up with two users who think they have a valid lock.

您要做的另一件事是确保您使用交易。您需要在数据库事务中包装锁的检查和设置,这样您最终不会得到两个认为具有有效锁的用户。

If you have tasks that require gaining locks on more than one resource (i.e. more than one record of a given type or more than one type of record) then you need to apply the locks in the same order wherever you do the locking. Otherwise you can have a dead lock, where one bit of code has record A locked and is wanting to lock record B and another bit of code has B locked and is waiting for record A.

如果您的任务需要在多个资源上获得锁定(即,给定类型的多个记录或多于一种类型的记录),则无论何时进行锁定,都需要以相同的顺序应用锁定。否则你可以有一个死锁,其中一位代码有记录A锁定并且想要锁定记录B而另一位代码已经B锁定并且正在等待记录A.

As to how you ensure locks aren't released unexpectedly. Make sure that if you have any long running process that could run longer than your lock timeout, that it refreshes its lock during its run.

至于如何确保锁定不会意外释放。如果您有任何长时间运行的进程可以运行超过锁定超时,请确保它在运行期间刷新其锁定。

The term "explicit locking" is also used to describe this time of locking.

术语“显式锁定”也用于描述锁定时间。

#2


I have done this manually.

我手动完成了这个。

  • Store the primary-key of the record to a lock table, and mark record mode attribute to edit.
  • 将记录的主键存储到锁定表,并标记记录模式属性以进行编辑。

  • When another user tries to select this record, indicate the user's ready only record.
  • 当另一个用户尝试选择此记录时,请指明用户的就绪记录。

  • Have a set-up maximum time for locking the records.
  • 设置锁定记录的最长时间。

  • Refresh page data for locked records. While an user is allowed to make changes, all other users are only allowed to check.
  • 刷新锁定记录的页面数据。虽然允许用户进行更改,但只允许所有其他用户进行检查。

Lock table should have design similar to this:

锁表的设计应与此类似:

User_ID, //who locked
Lock_start_Time,
Locked_Row_ID(Entity_ID), //this is primary key of the table of locked row.
Table_Name(Entity_Name) //table name of the locked row.

Remaining logic is something you have to figure out.

剩下的逻辑是你必须要弄清楚的。

This is just an idea which I implemented 4 years ago on special request of a client. After that client no one has asked me again to do anything similar, so I haven't achieved any other method.

这只是我在4年前根据客户的特殊要求实施的一个想法。在那个客户之后没有人再问过我做类似的事情,所以我还没有实现任何其他方法。

#1


It sounds like you are most of the way there. I don't think you really need LockRefreshedTime though, it doesn't really add anything. You may just as well use the LockAcquiredTime to decide when a lock has become stale.

听起来你大部分都在那里。我不认为你真的需要LockRefreshedTime,它并没有真正添加任何东西。你也可以使用LockAcquiredTime来决定锁何时变得陈旧。

The other thing you will want to do is make sure you make use of transactions. You need to wrap the checking and setting of the lock within a database transaction, so that you don't end up with two users who think they have a valid lock.

您要做的另一件事是确保您使用交易。您需要在数据库事务中包装锁的检查和设置,这样您最终不会得到两个认为具有有效锁的用户。

If you have tasks that require gaining locks on more than one resource (i.e. more than one record of a given type or more than one type of record) then you need to apply the locks in the same order wherever you do the locking. Otherwise you can have a dead lock, where one bit of code has record A locked and is wanting to lock record B and another bit of code has B locked and is waiting for record A.

如果您的任务需要在多个资源上获得锁定(即,给定类型的多个记录或多于一种类型的记录),则无论何时进行锁定,都需要以相同的顺序应用锁定。否则你可以有一个死锁,其中一位代码有记录A锁定并且想要锁定记录B而另一位代码已经B锁定并且正在等待记录A.

As to how you ensure locks aren't released unexpectedly. Make sure that if you have any long running process that could run longer than your lock timeout, that it refreshes its lock during its run.

至于如何确保锁定不会意外释放。如果您有任何长时间运行的进程可以运行超过锁定超时,请确保它在运行期间刷新其锁定。

The term "explicit locking" is also used to describe this time of locking.

术语“显式锁定”也用于描述锁定时间。

#2


I have done this manually.

我手动完成了这个。

  • Store the primary-key of the record to a lock table, and mark record mode attribute to edit.
  • 将记录的主键存储到锁定表,并标记记录模式属性以进行编辑。

  • When another user tries to select this record, indicate the user's ready only record.
  • 当另一个用户尝试选择此记录时,请指明用户的就绪记录。

  • Have a set-up maximum time for locking the records.
  • 设置锁定记录的最长时间。

  • Refresh page data for locked records. While an user is allowed to make changes, all other users are only allowed to check.
  • 刷新锁定记录的页面数据。虽然允许用户进行更改,但只允许所有其他用户进行检查。

Lock table should have design similar to this:

锁表的设计应与此类似:

User_ID, //who locked
Lock_start_Time,
Locked_Row_ID(Entity_ID), //this is primary key of the table of locked row.
Table_Name(Entity_Name) //table name of the locked row.

Remaining logic is something you have to figure out.

剩下的逻辑是你必须要弄清楚的。

This is just an idea which I implemented 4 years ago on special request of a client. After that client no one has asked me again to do anything similar, so I haven't achieved any other method.

这只是我在4年前根据客户的特殊要求实施的一个想法。在那个客户之后没有人再问过我做类似的事情,所以我还没有实现任何其他方法。