mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-10-30 19:52:03 +01:00
Phase 2: DATABASE_BACKEND switch, component config, schema scoping
- config: add DatabaseBackend enum + DATABASE_* component vars on CommonSettings (host/port/name/schema/user/password, defaults 'meshcorehub'); single effective_database_url resolver with precedence DATABASE_URL > postgres (assembled, fail-fast on missing vars) > SQLite default. effective_database_schema returns the schema only for Postgres. Collapses the duplicated resolver/field out of Collector/API settings. - database: create_database_engine/DatabaseManager accept a schema arg and scope Postgres connections via search_path (psycopg2 -c options; asyncpg server_settings). No-op for SQLite. - alembic/env: migrate into the instance schema (version_table_schema + CREATE SCHEMA + search_path) for Postgres; fix online render_as_batch to be SQLite-only (matching the offline path). - .env.example: document the DATABASE_* block. SQLite behaviour unchanged: default no-env path resolves to the same URL (new regression tests), full suite green (1051 passed), fresh db upgrade OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
9eab07d244
commit
f342f5bc70
5 files changed
+240
-52
No files matched your search
+30
-3
@@ -4,7 +4,7 @@ import os
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from sqlalchemy import engine_from_config, pool, text
|
||||
|
||||
from meshcore_hub.common.models import Base
|
||||
|
||||
@@ -41,6 +41,18 @@ def get_database_url() -> str:
|
||||
return config.get_main_option("sqlalchemy.url", "sqlite:///./meshcore.db")
|
||||
|
||||
|
||||
def get_schema(url: str) -> str | None:
|
||||
"""Postgres schema to migrate into, or None for SQLite.
|
||||
|
||||
Each Hub instance keeps its tables and alembic_version in its own schema so
|
||||
multiple instances (prod, stg, ...) can share one Postgres database with
|
||||
independent migration state.
|
||||
"""
|
||||
if url.startswith(("postgresql", "postgres")):
|
||||
return os.environ.get("DATABASE_SCHEMA", "meshcorehub")
|
||||
return None
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
@@ -53,6 +65,7 @@ def run_migrations_offline() -> None:
|
||||
script output.
|
||||
"""
|
||||
url = get_database_url()
|
||||
schema = get_schema(url)
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
@@ -61,6 +74,8 @@ def run_migrations_offline() -> None:
|
||||
# Batch mode is a SQLite-only workaround for its limited ALTER TABLE;
|
||||
# Postgres performs ALTERs directly.
|
||||
render_as_batch=url.startswith("sqlite"),
|
||||
version_table_schema=schema,
|
||||
include_schemas=schema is not None,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
@@ -74,7 +89,9 @@ def run_migrations_online() -> None:
|
||||
and associate a connection with the context.
|
||||
"""
|
||||
configuration = config.get_section(config.config_ini_section, {})
|
||||
configuration["sqlalchemy.url"] = get_database_url()
|
||||
url = get_database_url()
|
||||
configuration["sqlalchemy.url"] = url
|
||||
schema = get_schema(url)
|
||||
|
||||
connectable = engine_from_config(
|
||||
configuration,
|
||||
@@ -83,10 +100,20 @@ def run_migrations_online() -> None:
|
||||
)
|
||||
|
||||
with connectable.connect() as connection:
|
||||
# Ensure the instance's schema exists and scope this connection to it so
|
||||
# tables (and alembic_version) are created there. No-op for SQLite.
|
||||
if schema is not None:
|
||||
connection.execute(text(f'CREATE SCHEMA IF NOT EXISTS "{schema}"'))
|
||||
connection.execute(text(f'SET search_path TO "{schema}"'))
|
||||
connection.commit()
|
||||
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
render_as_batch=True, # SQLite batch mode for ALTER TABLE
|
||||
# Batch mode is a SQLite-only workaround for its limited ALTER TABLE.
|
||||
render_as_batch=url.startswith("sqlite"),
|
||||
version_table_schema=schema,
|
||||
include_schemas=schema is not None,
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
|
||||
Reference in new issue
Block a user