85 lines
3.2 KiB
Python
85 lines
3.2 KiB
Python
"""Phase 7S.1E email queue and retry metadata
|
|
|
|
Revision ID: 20260610_phase_7s1e_email_queue
|
|
Revises: 20260609_phase_7s1c_email_preferences
|
|
Create Date: 2026-06-10
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision = "20260610_phase_7s1e_email_queue"
|
|
down_revision = "20260609_phase_7s1c_email_preferences"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def _existing_columns(table_name: str) -> set[str]:
|
|
bind = op.get_bind()
|
|
inspector = sa.inspect(bind)
|
|
try:
|
|
return {col["name"] for col in inspector.get_columns(table_name)}
|
|
except Exception:
|
|
return set()
|
|
|
|
|
|
def _add_column_if_missing(table_name: str, column: sa.Column) -> None:
|
|
if column.name not in _existing_columns(table_name):
|
|
op.add_column(table_name, column)
|
|
|
|
|
|
def upgrade() -> None:
|
|
_add_column_if_missing("email_logs", sa.Column("attempt_count", sa.Integer(), nullable=False, server_default="0"))
|
|
_add_column_if_missing("email_logs", sa.Column("max_attempts", sa.Integer(), nullable=False, server_default="3"))
|
|
_add_column_if_missing("email_logs", sa.Column("queue_priority", sa.Integer(), nullable=False, server_default="100"))
|
|
_add_column_if_missing("email_logs", sa.Column("is_retryable", sa.Boolean(), nullable=False, server_default=sa.true()))
|
|
_add_column_if_missing("email_logs", sa.Column("queued_at", sa.DateTime(timezone=True), nullable=True))
|
|
_add_column_if_missing("email_logs", sa.Column("next_retry_at", sa.DateTime(timezone=True), nullable=True))
|
|
_add_column_if_missing("email_logs", sa.Column("last_attempt_at", sa.DateTime(timezone=True), nullable=True))
|
|
_add_column_if_missing("email_logs", sa.Column("processing_started_at", sa.DateTime(timezone=True), nullable=True))
|
|
|
|
existing_indexes = {idx["name"] for idx in sa.inspect(op.get_bind()).get_indexes("email_logs")}
|
|
for name, cols in {
|
|
"ix_email_logs_queue_priority": ["queue_priority"],
|
|
"ix_email_logs_is_retryable": ["is_retryable"],
|
|
"ix_email_logs_queued_at": ["queued_at"],
|
|
"ix_email_logs_next_retry_at": ["next_retry_at"],
|
|
"ix_email_logs_last_attempt_at": ["last_attempt_at"],
|
|
"ix_email_logs_processing_started_at": ["processing_started_at"],
|
|
}.items():
|
|
if name not in existing_indexes:
|
|
op.create_index(name, "email_logs", cols)
|
|
|
|
|
|
def downgrade() -> None:
|
|
# Safe downgrade for development only. SQLite may not support all drop-column
|
|
# operations depending on version, so production rollback should use backups.
|
|
for name in [
|
|
"ix_email_logs_processing_started_at",
|
|
"ix_email_logs_last_attempt_at",
|
|
"ix_email_logs_next_retry_at",
|
|
"ix_email_logs_queued_at",
|
|
"ix_email_logs_is_retryable",
|
|
"ix_email_logs_queue_priority",
|
|
]:
|
|
try:
|
|
op.drop_index(name, table_name="email_logs")
|
|
except Exception:
|
|
pass
|
|
for col in [
|
|
"processing_started_at",
|
|
"last_attempt_at",
|
|
"next_retry_at",
|
|
"queued_at",
|
|
"is_retryable",
|
|
"queue_priority",
|
|
"max_attempts",
|
|
"attempt_count",
|
|
]:
|
|
try:
|
|
op.drop_column("email_logs", col)
|
|
except Exception:
|
|
pass
|