One process owns this database — the image has run a single uvicorn worker for that reason since the four-engines bug — so a file beside the flows is the honest shape for it, and it is what lets `fluksio serve` need no infrastructure at all. Live values, node execution and the work queue never came here anyway; what does is a rollup a minute at a time, a row per cascade and the run history, and WAL keeps the readers going while that one writer works. DATA_DIR is now the one setting that moves everything an installation keeps; the rest derive from it and the images still spell theirs out. The schema is prepared in-process at startup, so the prestart service is gone, and the ten Postgres-only revisions collapse into one portable baseline. Three things only worked because psycopg was casting for us: a token's subject arriving as a string where the column is a UUID, `greatest`, and `date_bin`. The timestamps needed a column type of their own — SQLite stores no offset, and a naive datetime read back either raises against an aware `now` or serialises as local time. Postgres stays in the stack only for Umami, behind the analytics profile.
115 lines
3.3 KiB
Python
Executable File
115 lines
3.3 KiB
Python
Executable File
from logging.config import fileConfig
|
|
|
|
from alembic import context
|
|
from sqlalchemy import engine_from_config, pool
|
|
|
|
# this is the Alembic Config object, which provides
|
|
# access to the values within the .ini file in use.
|
|
config = context.config
|
|
|
|
# Interpret the config file for Python logging.
|
|
# This line sets up loggers basically.
|
|
# Driven from `fluksio.core.db` there is no ini file to read logging from.
|
|
if config.config_file_name is not None:
|
|
fileConfig(config.config_file_name)
|
|
|
|
# add your model's MetaData object here
|
|
# for 'autogenerate' support
|
|
# from myapp import mymodel
|
|
# target_metadata = mymodel.Base.metadata
|
|
# target_metadata = None
|
|
|
|
from fluksio.models import SQLModel # noqa
|
|
from fluksio.core.config import settings # noqa
|
|
from fluksio.core.types import UTCDateTime # noqa
|
|
|
|
target_metadata = SQLModel.metadata
|
|
|
|
|
|
def render_item(type_, obj, autogen_context):
|
|
"""Write the decorator out as the type it wraps.
|
|
|
|
A revision naming `UTCDateTime` would have to import from the app, which
|
|
ties a migration to a version of it. What it stores is a timezone-aware
|
|
timestamp, so that is what the revision says.
|
|
"""
|
|
if type_ == "type" and isinstance(obj, UTCDateTime):
|
|
autogen_context.imports.add("import sqlalchemy as sa")
|
|
return "sa.DateTime(timezone=True)"
|
|
return False
|
|
|
|
# other values from the config, defined by the needs of env.py,
|
|
# can be acquired:
|
|
# my_important_option = config.get_main_option("my_important_option")
|
|
# ... etc.
|
|
|
|
|
|
def get_url():
|
|
return str(settings.SQLALCHEMY_DATABASE_URI)
|
|
|
|
|
|
def run_migrations_offline():
|
|
"""Run migrations in 'offline' mode.
|
|
|
|
This configures the context with just a URL
|
|
and not an Engine, though an Engine is acceptable
|
|
here as well. By skipping the Engine creation
|
|
we don't even need a DBAPI to be available.
|
|
|
|
Calls to context.execute() here emit the given string to the
|
|
script output.
|
|
|
|
"""
|
|
url = get_url()
|
|
context.configure(
|
|
url=url, target_metadata=target_metadata, literal_binds=True, compare_type=True
|
|
)
|
|
|
|
with context.begin_transaction():
|
|
context.run_migrations()
|
|
|
|
|
|
def run_migrations_online():
|
|
"""Run migrations in 'online' mode.
|
|
|
|
In this scenario we need to create an Engine
|
|
and associate a connection with the context.
|
|
|
|
"""
|
|
# `fluksio.core.db.migrate` hands us the connection it is already inside;
|
|
# the CLI and `alembic` on the command line make their own.
|
|
connection = config.attributes.get("connection")
|
|
if connection is not None:
|
|
_run(connection)
|
|
return
|
|
|
|
configuration = config.get_section(config.config_ini_section)
|
|
configuration["sqlalchemy.url"] = get_url()
|
|
connectable = engine_from_config(
|
|
configuration,
|
|
prefix="sqlalchemy.",
|
|
poolclass=pool.NullPool,
|
|
)
|
|
|
|
with connectable.connect() as connection:
|
|
_run(connection)
|
|
|
|
|
|
def _run(connection):
|
|
context.configure(
|
|
connection=connection,
|
|
target_metadata=target_metadata,
|
|
compare_type=True,
|
|
render_item=render_item,
|
|
# SQLite cannot ALTER much: batch mode rebuilds the table instead.
|
|
render_as_batch=connection.dialect.name == "sqlite",
|
|
)
|
|
with context.begin_transaction():
|
|
context.run_migrations()
|
|
|
|
|
|
if context.is_offline_mode():
|
|
run_migrations_offline()
|
|
else:
|
|
run_migrations_online()
|