diff --git a/hindsight-api/hindsight_api/engine/memory_engine.py b/hindsight-api/hindsight_api/engine/memory_engine.py index 30d758c1..a5778453 100644 --- a/hindsight-api/hindsight_api/engine/memory_engine.py +++ b/hindsight-api/hindsight_api/engine/memory_engine.py @@ -1366,7 +1366,7 @@ class MemoryEngine(MemoryEngineInterface): self._file_storage = create_file_storage( storage_type=config.file_storage_type, pool_getter=lambda: self._pool, - schema=get_current_schema() if get_current_schema() != config.database_schema else None, + schema_getter=get_current_schema, ) logger.debug(f"File storage initialized ({config.file_storage_type})") diff --git a/hindsight-api/hindsight_api/engine/storage/__init__.py b/hindsight-api/hindsight_api/engine/storage/__init__.py index 6d3949b3..5fde54e0 100644 --- a/hindsight-api/hindsight_api/engine/storage/__init__.py +++ b/hindsight-api/hindsight_api/engine/storage/__init__.py @@ -12,6 +12,7 @@ def create_file_storage( storage_type: str, pool_getter: Callable | None = None, schema: str | None = None, + schema_getter: Callable | None = None, **kwargs, ) -> FileStorage: """ @@ -20,7 +21,8 @@ def create_file_storage( Args: storage_type: "native" (PostgreSQL BYTEA) or "s3" (S3-compatible object storage) pool_getter: Database pool getter (required for native) - schema: Database schema (for native multi-tenant) + schema: Static database schema (for native single-tenant) + schema_getter: Callable returning current schema at query time (for native multi-tenant) **kwargs: Additional args passed to storage backend Returns: @@ -32,7 +34,7 @@ def create_file_storage( if storage_type == "native": if not pool_getter: raise ValueError("pool_getter required for native (PostgreSQL) storage") - return PostgreSQLFileStorage(pool_getter=pool_getter, schema=schema) + return PostgreSQLFileStorage(pool_getter=pool_getter, schema=schema, schema_getter=schema_getter) elif storage_type == "s3": from ...config import get_config from .s3 import S3FileStorage diff --git a/hindsight-api/hindsight_api/engine/storage/postgresql.py b/hindsight-api/hindsight_api/engine/storage/postgresql.py index 9dafdfd2..91f5e1c9 100644 --- a/hindsight-api/hindsight_api/engine/storage/postgresql.py +++ b/hindsight-api/hindsight_api/engine/storage/postgresql.py @@ -40,16 +40,30 @@ class PostgreSQLFileStorage(FileStorage): For production/scale, consider S3FileStorage instead. """ - def __init__(self, pool_getter: Callable[[], "asyncpg.Pool"], schema: str | None = None): + def __init__( + self, + pool_getter: Callable[[], "asyncpg.Pool"], + schema: str | None = None, + schema_getter: Callable[[], str] | None = None, + ): """ Initialize PostgreSQL file storage. Args: pool_getter: Function that returns asyncpg connection pool - schema: Database schema (for multi-tenant support) + schema: Static database schema (fallback for single-tenant / tests) + schema_getter: Callable returning current schema at query time (for multi-tenant) """ self._pool_getter = pool_getter - self._schema = schema + self._static_schema = schema + self._schema_getter = schema_getter + + @property + def _schema(self) -> str | None: + """Resolve schema dynamically per-request when schema_getter is provided.""" + if self._schema_getter: + return self._schema_getter() + return self._static_schema async def store( self,