refactor(db)!: finalize pooled WNDB v2 migration
This commit is contained in:
@@ -1,12 +1,102 @@
|
||||
from app.infra.db.dynamic_manager import ProjectConnectionManager
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from app.infra.db import dynamic_manager
|
||||
|
||||
|
||||
def test_normalize_pg_url_preserves_password():
|
||||
manager = ProjectConnectionManager()
|
||||
class FakeAsyncPool:
|
||||
created: list["FakeAsyncPool"] = []
|
||||
|
||||
url = manager._normalize_pg_url(
|
||||
"postgresql://tjwater:secret@192.168.1.114:5433/tjwater"
|
||||
)
|
||||
def __init__(self, **kwargs) -> None:
|
||||
self.kwargs = kwargs
|
||||
self.closed = False
|
||||
self.created.append(self)
|
||||
|
||||
assert url == "postgresql+psycopg://tjwater:secret@192.168.1.114:5433/tjwater"
|
||||
assert "***" not in url
|
||||
async def open(self) -> None:
|
||||
return None
|
||||
|
||||
async def close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
@asynccontextmanager
|
||||
async def connection(self):
|
||||
yield object()
|
||||
|
||||
|
||||
def test_active_project_pool_is_not_evicted(monkeypatch) -> None:
|
||||
async def exercise() -> None:
|
||||
manager = dynamic_manager.ProjectConnectionManager()
|
||||
first_id = uuid4()
|
||||
second_id = uuid4()
|
||||
|
||||
async with manager.pg_connection(first_id, "biz_data", "dsn-1", 1, 2):
|
||||
first_pool = manager._pg_raw_cache[
|
||||
dynamic_manager.CacheKey(first_id, "biz_data")
|
||||
].pool
|
||||
async with manager.pg_connection(
|
||||
second_id, "biz_data", "dsn-2", 1, 2
|
||||
):
|
||||
assert first_pool.closed is False
|
||||
assert len(manager._pg_raw_cache) == 2
|
||||
|
||||
assert first_pool.closed is False
|
||||
assert list(manager._pg_raw_cache) == [
|
||||
dynamic_manager.CacheKey(first_id, "biz_data")
|
||||
]
|
||||
|
||||
await manager.close_all()
|
||||
|
||||
FakeAsyncPool.created = []
|
||||
monkeypatch.setattr(dynamic_manager, "AsyncConnectionPool", FakeAsyncPool)
|
||||
monkeypatch.setattr(dynamic_manager.settings, "PROJECT_PG_CACHE_SIZE", 1)
|
||||
asyncio.run(exercise())
|
||||
|
||||
|
||||
def test_active_pool_uses_generation_replacement(monkeypatch) -> None:
|
||||
async def exercise() -> None:
|
||||
manager = dynamic_manager.ProjectConnectionManager()
|
||||
project_id = uuid4()
|
||||
|
||||
async with manager.pg_connection(
|
||||
project_id, "biz_data", "old-dsn", 1, 2
|
||||
):
|
||||
old_pool = FakeAsyncPool.created[0]
|
||||
async with manager.pg_connection(
|
||||
project_id, "biz_data", "new-dsn", 1, 2
|
||||
):
|
||||
assert old_pool.closed is False
|
||||
assert len(manager._retired_pg) == 1
|
||||
|
||||
assert old_pool.closed is True
|
||||
assert manager._retired_pg == []
|
||||
|
||||
await manager.close_all()
|
||||
|
||||
FakeAsyncPool.created = []
|
||||
monkeypatch.setattr(dynamic_manager, "AsyncConnectionPool", FakeAsyncPool)
|
||||
asyncio.run(exercise())
|
||||
|
||||
|
||||
def test_close_project_does_not_interrupt_active_borrow(monkeypatch) -> None:
|
||||
async def exercise() -> None:
|
||||
manager = dynamic_manager.ProjectConnectionManager()
|
||||
project_id = uuid4()
|
||||
key = dynamic_manager.CacheKey(project_id, "iot_data")
|
||||
|
||||
async with manager.timescale_connection(
|
||||
project_id, "iot_data", "ts-dsn", 1, 2
|
||||
):
|
||||
pool = manager._ts_cache[key].pool
|
||||
assert await manager.close_project(project_id) is False
|
||||
assert pool.closed is False
|
||||
|
||||
assert await manager.close_project(project_id) is True
|
||||
assert pool.closed is True
|
||||
assert key not in manager._ts_cache
|
||||
|
||||
FakeAsyncPool.created = []
|
||||
monkeypatch.setattr(dynamic_manager, "AsyncConnectionPool", FakeAsyncPool)
|
||||
asyncio.run(exercise())
|
||||
|
||||
Reference in New Issue
Block a user