refactor(db)!: finalize pooled WNDB v2 migration

This commit is contained in:
2026-08-27 17:26:22 +08:00
parent fa188af0b1
commit b74799a39d
105 changed files with 4988 additions and 5565 deletions
+74 -1
View File
@@ -1,5 +1,5 @@
import asyncio
from contextlib import contextmanager
from contextlib import asynccontextmanager, contextmanager
from datetime import datetime, timezone
from app.infra.db.timescaledb.repositories.realtime import RealtimeRepository
@@ -71,12 +71,23 @@ def test_get_nodes_by_time_range_normalizes_inputs_to_utc():
class _SyncTransactionConnection:
def __init__(self):
self.transactions = 0
self.calls: list[tuple[str, tuple]] = []
@contextmanager
def transaction(self):
self.transactions += 1
yield
@contextmanager
def cursor(self):
connection = self
class Cursor:
def execute(self, query, params):
connection.calls.append((query, params))
yield Cursor()
def test_realtime_node_and_link_replacement_share_outer_transaction(monkeypatch):
conn = _SyncTransactionConnection()
@@ -101,3 +112,65 @@ def test_realtime_node_and_link_replacement_share_outer_transaction(monkeypatch)
assert conn.transactions == 1
assert calls == ["nodes", "links"]
assert [query for query, _params in conn.calls] == [
"SELECT pg_advisory_xact_lock(hashtextextended(%s::text, 1))",
"DELETE FROM realtime.node_results WHERE time = %s",
"DELETE FROM realtime.link_results WHERE time = %s",
]
def test_realtime_batch_rejects_multiple_timestamps_before_writing():
data = [
{"time": "2026-06-01T00:00:00Z", "id": "N1"},
{"time": "2026-06-01T00:15:00Z", "id": "N2"},
]
try:
RealtimeRepository.insert_nodes_batch_sync(object(), data)
except ValueError as exc:
assert str(exc) == "Realtime batch must contain exactly one timestamp"
else:
raise AssertionError("multiple realtime timestamps were accepted")
class _AsyncEmptySnapshotConnection:
def __init__(self):
self.calls: list[tuple[str, tuple]] = []
@asynccontextmanager
async def transaction(self):
yield
def cursor(self):
connection = self
class Cursor:
async def __aenter__(self):
return self
async def __aexit__(self, *_args):
return False
async def execute(self, query, params):
connection.calls.append((query, params))
return Cursor()
def test_empty_realtime_side_is_deleted_as_part_of_snapshot_replacement():
conn = _AsyncEmptySnapshotConnection()
asyncio.run(
RealtimeRepository.store_realtime_simulation_result(
conn,
node_result_list=[],
link_result_list=[],
result_start_time="2026-06-01T00:00:00Z",
)
)
assert [query for query, _params in conn.calls] == [
"SELECT pg_advisory_xact_lock(hashtextextended(%s::text, 1))",
"DELETE FROM realtime.node_results WHERE time = %s",
"DELETE FROM realtime.link_results WHERE time = %s",
]