TL; DR
Этот сценарий будет соответствовать ожиданиям.
Объяснение
Когда маловероятное событие двух экземпляров сценария пытается записать вв это же время первый блокирует базу данных, а второй некоторое время молча ждет, пока первый завершит свою транзакцию, чтобы база данных была разблокирована для повторной записи.
Точнее, второй экземпляр сценарияждет в течение 5 секунд (по умолчанию), а затем вызывает OperationalError
с сообщением database is locked
.Как заметил @roganjosh, это поведение на самом деле специфично для оболочки Python SQLite.В документации говорится :
Когда к базе данных получают доступ несколько соединений, и один из процессов изменяет базу данных, база данных SQLite блокируется до тех пор, пока эта транзакция не будет зафиксирована.Параметр timeout указывает, как долго соединение должно ожидать, пока блокировка не исчезнет, до возникновения исключения.Значение по умолчанию для параметра времени ожидания - 5,0 (пять секунд).
Тесты
Чтобы продемонстрировать событие столкновения двух экземпляров, я изменил функцию main
:
def main():
c = sqlite3.connect(FILENAME)
c.execute(DB_CREATE_TABLE)
c.commit()
print('{} {}: {}'.format(time.time(), sys.argv[1], 'trying to insert ...'))
try:
c.execute(DB_INSERT, (sys.argv[1],))
except sqlite3.OperationalError as e:
print('{} {}: {}'.format(time.time(), sys.argv[1], e))
return
time.sleep(int(sys.argv[2]))
c.commit()
print('{} {}: {}'.format(time.time(), sys.argv[1], 'done'))
c.close()
В документации говорится, что база данных заблокирована, пока транзакция не будет зафиксирована.Так что простого сна во время транзакции должно быть достаточно для ее проверки.
Тест 1
Мы запускаем следующую команду:
python3 test.py first 10 & sleep 1 && python3 test.py second 0
Первый экземпляр запускается и после1с запускается второй экземпляр.Первый экземпляр создает транзакцию длиной 10 с, во время которой второй пытается выполнить запись в базу данных, ожидает и затем вызывает исключение.Журнал демонстрирует, что:
1540307088.6203635 first: trying to insert ...
1540307089.6155508 second: trying to insert ...
1540307094.6333485 second: database is locked
1540307098.6353421 first: done
Тест 2
Мы выполняем следующую команду:
python3 test.py first 3 & sleep 1 && python3 test.py second 0
Первый экземпляр запускается, а через 1 секунду второй экземплярбыть запущенным.Первый экземпляр создает транзакцию длиной 3 с, во время которой второй пытается выполнить запись в базу данных и ожидает.Поскольку он был создан после 1 с, он должен ждать 3 с - 1 с = 2 с, что меньше 5 с по умолчанию, поэтому обе транзакции завершатся успешно.Журнал демонстрирует, что:
1540307132.2834115 first: trying to insert ...
1540307133.2811155 second: trying to insert ...
1540307135.2912169 first: done
1540307135.3217440 second: done
Заключение
Время, необходимое для завершения транзакции, значительно меньше (миллисекунды), чем ограничение времени блокировки (5 с), поэтому в этом сценарии сценарий действительносоответствует ожиданиям.Но как @HarlyH.прокомментировал, что транзакции ждут в очереди для принятия, поэтому для интенсивно используемой или очень большой базы данных это не хорошее решение, так как связь с базой данных станет медленной.