Split databases and Redis — prepare infrastructure for per-domain isolation
Redis: per-app DB index (0-5) with shared auth DB 15 for SSO keys; flushdb replaces flushall so deploys don't wipe cross-app auth state. Postgres: drop 13 cross-domain FK constraints (migration v2t0p8q9r0), remove dead ORM relationships, add explicit joins for 4 live ones. Multi-engine sessions (account + federation) ready for per-domain DBs via DATABASE_URL_ACCOUNT / DATABASE_URL_FEDERATION env vars. All URLs initially point to the same appdb — zero behaviour change until split-databases.sh is run to migrate data to per-domain DBs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,9 @@ A dedicated asyncpg LISTEN connection wakes the poll loop immediately when
|
||||
emit_activity() fires NOTIFY ap_activity_pending, so latency drops from
|
||||
~2 seconds (poll interval) to sub-100 ms. The fixed-interval poll remains
|
||||
as a safety-net fallback.
|
||||
|
||||
The LISTEN connection and poll queries target the federation database
|
||||
(DATABASE_URL_FEDERATION) since ap_activities lives there.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -21,7 +24,7 @@ import asyncpg
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from shared.db.session import get_session, DATABASE_URL
|
||||
from shared.db.session import get_federation_session, DATABASE_URL_FEDERATION
|
||||
from shared.models.federation import APActivity
|
||||
from .bus import get_activity_handlers
|
||||
|
||||
@@ -89,7 +92,7 @@ class EventProcessor:
|
||||
|
||||
async def _listen_for_notify(self) -> None:
|
||||
"""Maintain a LISTEN connection and wake the poll loop on NOTIFY."""
|
||||
dsn = DATABASE_URL.replace("+asyncpg", "")
|
||||
dsn = DATABASE_URL_FEDERATION.replace("+asyncpg", "")
|
||||
while self._running:
|
||||
try:
|
||||
self._listen_conn = await asyncpg.connect(dsn)
|
||||
@@ -154,7 +157,7 @@ class EventProcessor:
|
||||
"""
|
||||
cutoff = datetime.now(timezone.utc) - timedelta(seconds=self._stuck_timeout)
|
||||
try:
|
||||
async with get_session() as session:
|
||||
async with get_federation_session() as session:
|
||||
filters = [
|
||||
APActivity.process_state == "processing",
|
||||
APActivity.created_at < cutoff,
|
||||
@@ -180,7 +183,7 @@ class EventProcessor:
|
||||
async def _process_batch(self) -> int:
|
||||
"""Fetch and process a batch of pending activities. Returns count processed."""
|
||||
processed = 0
|
||||
async with get_session() as session:
|
||||
async with get_federation_session() as session:
|
||||
filters = [
|
||||
APActivity.process_state == "pending",
|
||||
APActivity.process_attempts < APActivity.process_max_attempts,
|
||||
|
||||
Reference in New Issue
Block a user