refactor(db)!: adopt project-routed pooled databases

Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
This commit is contained in:
2026-08-25 18:35:05 +08:00
parent fdbcc5c033
commit fa188af0b1
181 changed files with 8446 additions and 33546 deletions
+152
View File
@@ -0,0 +1,152 @@
from contextlib import contextmanager
import pytest
from app.infra.db.timescaledb import sync_pool
class _FakePool:
def __init__(self, *, conninfo, **_kwargs):
self.conninfo = conninfo
self.closed = False
self.borrowed = 0
self.returned = 0
def close(self):
self.closed = True
@contextmanager
def connection(self):
self.borrowed += 1
try:
yield object()
finally:
self.returned += 1
@pytest.fixture(autouse=True)
def clear_pools():
sync_pool._pools.clear()
sync_pool._pool_conninfo.clear()
sync_pool._pool_borrows.clear()
yield
sync_pool._pools.clear()
sync_pool._pool_conninfo.clear()
sync_pool._pool_borrows.clear()
def test_pool_reuses_same_routed_timescale_dsn(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: f"dbname={db_name}",
)
first = sync_pool.get_timescale_pool("tjwater_next")
second = sync_pool.get_timescale_pool("tjwater_next")
assert first is second
def test_pool_rebuilds_after_routing_change(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
dsn = {"value": "host=old dbname=tjwater_next"}
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: dsn["value"],
)
old = sync_pool.get_timescale_pool("tjwater_next")
dsn["value"] = "host=new dbname=tjwater_next"
new = sync_pool.get_timescale_pool("tjwater_next")
assert old.closed is True
assert new is not old
def test_connection_is_returned_to_pool(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: f"dbname={db_name}",
)
pool = sync_pool.get_timescale_pool("tjwater_next")
with sync_pool.timescale_connection("tjwater_next"):
assert pool.borrowed == 1
assert pool.returned == 0
assert pool.returned == 1
def test_close_removes_pool(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: f"dbname={db_name}",
)
pool = sync_pool.get_timescale_pool("tjwater_next")
sync_pool.close_timescale_pool("tjwater_next")
assert pool.closed is True
assert "tjwater_next" not in sync_pool._pools
def test_close_all_removes_every_pool(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: f"dbname={db_name}",
)
first = sync_pool.get_timescale_pool("first")
second = sync_pool.get_timescale_pool("second")
sync_pool.close_all_timescale_pools()
assert first.closed is True
assert second.closed is True
assert sync_pool._pools == {}
def test_pool_cache_evicts_least_recently_used_idle_pool(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
monkeypatch.setattr(sync_pool.settings, "PROJECT_TS_CACHE_SIZE", 2)
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: f"dbname={db_name}",
)
first = sync_pool.get_timescale_pool("first")
second = sync_pool.get_timescale_pool("second")
sync_pool.get_timescale_pool("first")
third = sync_pool.get_timescale_pool("third")
assert list(sync_pool._pools) == ["first", "third"]
assert second.closed is True
assert first.closed is False
assert third.closed is False
def test_pool_cache_does_not_evict_active_pool(monkeypatch):
monkeypatch.setattr(sync_pool, "ConnectionPool", _FakePool)
monkeypatch.setattr(sync_pool.settings, "PROJECT_TS_CACHE_SIZE", 1)
monkeypatch.setattr(
sync_pool,
"get_project_timescale_pgconn_string",
lambda *, db_name: f"dbname={db_name}",
)
with sync_pool.timescale_connection("active"):
active = sync_pool._pools["active"]
sync_pool.get_timescale_pool("new")
assert active.closed is False
assert set(sync_pool._pools) == {"active", "new"}
assert list(sync_pool._pools) == ["new"]
assert active.closed is True