Skip to content

Commit

Permalink
add acid test which should be made passed
Browse files Browse the repository at this point in the history
  • Loading branch information
kalombos committed Mar 22, 2024
1 parent de744fb commit ba4e83f
Showing 1 changed file with 71 additions and 0 deletions.
71 changes: 71 additions & 0 deletions tests/test_transaction.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import asyncio

import pytest

from tests.conftest import all_dbs
from tests.models import TestModel

Expand Down Expand Up @@ -81,3 +83,72 @@ async def t3():
await asyncio.sleep(0.125)

await asyncio.gather(t1(), t2(), t3())


@all_dbs
@pytest.mark.skip
async def test_acid_when_connetion_has_been_brooken(manager):
# TODO make this test passed
async def restart_connections(event_for_lock: asyncio.Event) -> None:
event_for_lock.set()
# С этого момента БД доступна, пулл в порядке, всё хорошо. Таски могут работать работу
await asyncio.sleep(0.05)

# Ниже происходит падение метеорита
# (приложением обнаруживается разрыв коннекта с БД и оно сливает пулл + заполняет заново)
# (может выглядеть нереалистично, но такое бывает и на проде, мы просто симулируем это редкое событие)
#
# Мы через event запрещаем таскам трогать БД на время переподнятия пулла.
# Это нужно, чтобы воспроизвести очень редкие условия, при которых peewee-async косячит.
event_for_lock.clear()

await manager.database.close_async()
await manager.database.connect_async()

event_for_lock.set()

# БД самопочинилась (пулл заполнен и готов к работе).
# С этого момента таски опять могут работать работу
return None

async def insert_records(event_for_wait: asyncio.Event):
await event_for_wait.wait()
async with manager.transaction():
# BEGIN
# INSERT 1
await manager.create(TestModel, text="1")

# Это место для падения метеорита.
# Тут произойдёт разрыв соединения и подключение заново.
# event здесь нужен чтобы таска не упала с исключением, а воспроизвела редкое поведение peewee-async
await asyncio.sleep(0.05)
await event_for_wait.wait()
# # Метеорит позади. event-loop вернул управление в таску
#
# # INSERT 2
await manager.create(TestModel, text="2")
# END ?

return None


# Этот event-семафор нужно чтобы удобно воссоздать редкую ситуацию,
# когда разрыв + восстановление происходит до того, как в asyncio.Task вернётся управление.
# В дикой природе такое происходит редко и при определённых стечениях обстоятельств,
# но приносит ощутимый ущерб
event = asyncio.Event()

results = await asyncio.gather(
restart_connections(event),
insert_records(event),
return_exceptions=True,
)
# Проверяем, что ни одна из тасок не упала с исключением
# assert results == [None, None]

# (!) Убеждаемся, что атомарность работает (!)
# Т.е. у нас должны либо закоммититься 2 записи, либо ни одной
a = list(await manager.execute(TestModel.select()))
assert len(a) in (0, 2), f'WTF, peewee-async ?! Saved rows: {a}'
# Если assert выше упал, то в БД оказалась 1 запись, а не 0 или 2.
# Хотя мы на уровне кода пытались гарантировать, что такого не будет

0 comments on commit ba4e83f

Please sign in to comment.