Files
arrr-erp/alembic/versions/20260610_phase_7s1e_email_queue_retry.py
2026-06-20 15:01:44 +05:30

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