#python #django #sqlite #django-models
Вопрос:
В настоящее время я пишу API, в котором пользователь может переводить деньги со своего аккаунта на телефон, но я сталкиваюсь с проблемой, которая заключается в том, что пользователь может позвонить в API несколько раз одновременно и попросить перевести деньги. Обычно баланс его счета был бы неправильным после звонков, поэтому я много искал и выяснил, что могу использовать атомарные транзакции и блокировать базу данных. Теперь, если пользователь использует API, например, дважды, один из них работает правильно, но другой получает django.db.utils.OperationalError: database is locked
ошибку. Есть идеи, как я могу правильно обращаться с ними обоими? (Я мог бы использоватьпока и подождите, пока база данных не будет разблокирована, но я предпочел бы этого не делать)
models.py
@transaction.atomic()
def withdraw(self, amount, phone, username):
property = transferLog.objects.filter(username=username).order_by('-time').select_for_update()[0]
if property.property >= int(amount):
self.username = username
self.property = property.property - int(amount)
self._from = username
self._to = phone
self.amount = int(amount)
self.time = str(datetime.datetime.now())
self.save()
return {'result': 'success', 'message': 'transfer complete', 'remaining': self.property}
else:
return {'result': 'error', 'message': 'not enough money'}
Комментарии:
1. Вероятно, вам следует перейти с SQLite на другого поставщика баз данных, например PostgreSQL. SQLite не поддерживает очень высокий уровень параллелизма,
2. Да, я изменил базу данных, и это сработало. Спасибо!
Ответ №1:
Я предполагаю, что вы используете базу данных SQLite. Он не допускает одновременной записи, т. е. более одной операции записи в данный момент времени.
У вас есть варианты:
- Либо переключитесь на другую базу данных
- Или увеличьте параметр базы
timeout
данных в настройках
Пример:
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'db.sqlite3',
'OPTIONS': {
'timeout': 60, # 1 minute
}
}
}
Это не масштабируемое решение. Предположим, что у вас есть много процессов, ожидающих доступа на запись, вы все еще можете получить OperationalError
, если какие-либо процессы все еще ждут через 60 секунд.
Комментарии:
1. Я увеличил время ожидания. Ошибка исчезла, но баланс не изменился. Я думаю, что мне следует перейти на PostgreSQL.
2. @Mfr Трудно сказать. Вы пытались отладить код? Работает ли он так, как ожидалось? Но да, если ваш проект требует одновременной записи, то лучше отказаться от SQLite.
3. да, я отлаживаю его часами 🙂 Вы правы, SQLite мне не подходит. Все равно спасибо!