Мне интересно, как бороться с параллелизмом, учитывая общую книгу. Рассмотрим схему, как это:Общая паровая игра (django atomic operations)
id | account_id | credit | debit | balance |
1 | 123 | 0 | 100 | 200 |
2 | 456 | 100 | 0 | 100 |
Чтобы добавить новую запись в бухгалтерской книге, я бы сделал (псевдо-код):
last_entry = get last account entry
is_credit = figure out if it is debit or credit entry
is_liability = figure out type of account
new_entry = Entry(
foo='bar'
# etc
)
if is_liability and is_credit
new_entry.balance = last_entry.balance + amount
if is_liability and !is_credit
new_entry.balance = last_entry.balance - amount
if !is_liability and is_credit
new_entry.balance = last_entry.balance - amount
if !is_liability and !is_credit
new_entry.balance = last_entry.balance + amount
new_entry.save()
Проблема, которую я вижу при таком подходе является:
Давайте скажем, приходит запрос, и я должен ввести новую запись в книгу. Новая запись увеличит баланс аккаунта.
Что делать, если в середине запуска вышеуказанного кода (скажем, после получения последней записи) есть еще один запрос, который снова увеличит весы.
Так баланс будет увеличен один раз, другой запрос будет сохранить новую запись с тем же балансом, как он будет просто использовать что-то вроде:
new_balance = last_entry.balance + amount
Но last_entry был устаревшим другим запросом так баланс сейчас выше.
Любые идеи о том, как убедиться, что подобной ситуации не происходит (я знаю, что это было бы маловероятно).
UPDATE:
После некоторых ответов, я пришел к этому решению с помощью SELECT FOR UPDATE:
with transaction.atomic():
new_entries = prepare_entries()
for new_entry in new_entries:
new_entry.save()
Это хороший способ, чтобы обойти потенциальные проблемы параллелизма?
Можете взглянуть на мой код? Я добавил несколько пример кода на мой вопрос. Вы видите какие-либо проблемы с этим? –
Спасибо. Я вижу сейчас. Таким образом, изменения, сделанные одной открытой транзакцией, видны другим работающим транзакциям. Я обновил свой пример кода. Сейчас это намного проще. –
Обратите внимание, что для работы 'select_for_update' вы должны находиться внутри транзакции. –