""" control/base.py --------------- Standalone SQLAlchemy foundation for the control plane. `ControlBase` keeps control-plane table metadata fully separate from the tenant-schema models declared on `app.db.Model`. The engine is built lazily from the `CONTROL_DATABASE_URL` environment variable. """ import os from contextlib import contextmanager from sqlalchemy import create_engine from sqlalchemy.orm import declarative_base, sessionmaker # Every control model subclasses this base — its own metadata collection. ControlBase = declarative_base() def get_control_database_url() -> str: """Return CONTROL_DATABASE_URL or raise a clear error if unset.""" url = os.environ.get('CONTROL_DATABASE_URL') if not url: raise RuntimeError( "CONTROL_DATABASE_URL is not set. Example: " "mysql+pymysql://jqc_control:password@localhost/jqc_control" ) return url _engine = None _Session = None def get_engine(): """Lazily build (and cache) the control-plane engine.""" global _engine if _engine is None: _engine = create_engine( get_control_database_url(), pool_pre_ping=True, pool_recycle=1800, future=True, ) return _engine def get_sessionmaker(): """Lazily build (and cache) the control-plane sessionmaker.""" global _Session if _Session is None: _Session = sessionmaker( bind=get_engine(), future=True, expire_on_commit=False ) return _Session @contextmanager def control_session(): """Context-managed session — commits on success, rolls back on error. Usage: with control_session() as s: s.add(obj) """ session = get_sessionmaker()() try: yield session session.commit() except Exception: session.rollback() raise finally: session.close()