Optimistic Django Transaction "Lock"

I have a function fn() , which should atomize working with a database, which relies on a certain set of data that does not change during its execution (though most of the time).

What is the correct way to implement this in Django? Basically I would 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 (the database should only see the state before fn runs or after fn runs successfully), but I'm not sure if there is a good primitive to execute commit_only_if_the_data_actually_didnt_change and retry the operation if it fails.

To verify that the data has not changed, just check that the number of returned items for the request is the same as at the beginning of the function; however, I do not know if there are any primitives that allow you to check and make decisions at the same time / without race conditions.

0
source share
1 answer

If you are in a transaction block, the only thing that can change the data you are reading is other operations inside the same transaction block . Until fn() makes any changes to data , you are guaranteed that the data will not change if fn() does not change it. This is a problem that transactions must solve.

If data can vary within fn() , just keep track of where it changes, or keep track of the end 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 until the loop is like this:

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

Source: https://habr.com/ru/post/1015355/


All Articles