Optimistic “locking” with Django transactions

断了今生、忘了曾经 提交于 2019-12-08 11:55:58

问题


I have a function fn() that needs to atomically do some database work that relies on some set of data not changing during its execution (true most of the time).

What is the correct way to implement this in Django? Basically I'd like to do something like this:

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 takes care of part of the problem (database should only ever see the state before fn runs or after fn runs successfully), but I'm not sure if there exists a good primitive to do the commit_only_if_the_data_actually_didnt_change, and retrying the operation if it fails.

To verify the data didn't change, it would be enough to just check the count of items returned for a query is the same as it was at the beginning of the function; however, I don't know if there are any primitives that let you make the check and commit decision at the same time / without race condition.


回答1:


If you are in a transaction block, the only thing that can change the data that you are reading are other operations within that same transaction block. So as long as fn() does not make any changes to data, you are guaranteed that the data will not change unless fn() changes it. That is the problem that transactions are meant to solve.

If data can change within the confines of fn() just keep track of the places where it changes or keep track of the final result.

@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()

And then handle the exception in you while loop like so:

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


来源:https://stackoverflow.com/questions/41798462/optimistic-locking-with-django-transactions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!