乐观 "locking" 与 Django 交易

Optimistic "locking" with Django transactions

我有一个函数 fn() 需要自动执行一些数据库工作,这些工作依赖于在执行期间不会更改的某些数据集(大多数情况下都是如此)。

在 Django 中实现它的正确方法是什么?基本上我想做这样的事情:

def ensure_fn_runs_successfully():
  # While commit unsuccessful, keep trying
  while not fn():
    pass

@transaction.atomic
def fn():
  data = read_data_that_must_not_change()

  ... do some operations with the data and perform database operations ...

  # Assume it returns true if commit was successful, otherwise false
  return commit_only_if_the_data_actually_didnt_change()

@transaction.atomic 解决了部分问题(数据库应该只看到 fn 运行之前或 fn 成功运行之后的状态),但我不确定是否存在执行 commit_only_if_the_data_actually_didnt_change 并在失败时重试操作的良好原语。

要验证数据没有改变,只需检查查询返回的项目数是否与函数开始时的相同即可;但是,我不知道是否有任何原语可以让您在没有竞争条件的情况下同时做出检查和提交决定。

如果您在一个事务块中,唯一可以更改您正在读取的数据的是同一事务块中的其他操作。所以只要fn()不对data做任何改动,就可以保证除非fn()改动,否则数据不会改变。这就是交易要解决的问题。

如果 data 可以在 fn() 的范围内更改,只需跟踪更改的位置或跟踪最终结果。

@transaction.atomic
def fn():
  data = read_data_that_must_not_change()
  original_data = copy.copy(data)
  ... do some operations with the data and perform database operations ...

  # Assume it returns true if commit was successful, otherwise false
  if data != original_data:
    raise Exception('Oh no!  Data changed!') 
    # raising in exception is how you prevent transaction.atomic
    # from committing
  return commit_only_if_the_data_actually_didnt_change()

然后像这样处理 while 循环中的异常:

while True:
    try:
        fn()
        break
    except:
        time.sleep(10) # ten second cool off
        pass