feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
@@ -0,0 +1,724 @@
|
||||
"""add immutable commercial billing periods and administration audit
|
||||
|
||||
Revision ID: 20260716_0021
|
||||
Revises: 20260716_0020
|
||||
Create Date: 2026-07-16 22:45:00
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "20260716_0021"
|
||||
down_revision: str | None = "20260716_0020"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def _require_postgresql() -> None:
|
||||
dialect_name = op.get_bind().dialect.name
|
||||
if dialect_name != "postgresql":
|
||||
raise RuntimeError(
|
||||
"20260716_0021 only supports PostgreSQL; "
|
||||
f"refusing to mutate {dialect_name} without transactional constraint DDL"
|
||||
)
|
||||
|
||||
|
||||
def _require_empty_billing_lifecycle_for_downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
counts = {
|
||||
table_name: int(bind.scalar(sa.text(f"SELECT COUNT(*) FROM {table_name}")) or 0)
|
||||
for table_name in ("commercial_admin_events", "commercial_billing_periods")
|
||||
}
|
||||
if any(counts.values()):
|
||||
summary = ", ".join(f"{name}={count}" for name, count in counts.items())
|
||||
raise RuntimeError(
|
||||
"cannot downgrade commercial billing lifecycle: immutable periods or audit "
|
||||
f"facts exist ({summary})"
|
||||
)
|
||||
|
||||
|
||||
def _json_object_default() -> sa.TextClause:
|
||||
return sa.text("'{}'::json")
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
_require_postgresql()
|
||||
_create_billing_periods()
|
||||
_create_admin_events()
|
||||
_backfill_current_periods_and_audit()
|
||||
_bind_usage_to_periods()
|
||||
_bind_costs_to_periods()
|
||||
_bind_reservations_to_periods()
|
||||
_create_period_overlap_guard()
|
||||
_create_append_only_triggers()
|
||||
|
||||
|
||||
def _create_billing_periods() -> None:
|
||||
op.create_table(
|
||||
"commercial_billing_periods",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("subscription_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("plan_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("period_sequence", sa.Integer(), nullable=False),
|
||||
sa.Column("period_key", sa.String(length=64), nullable=False),
|
||||
sa.Column("status", sa.String(length=16), nullable=False, server_default="issued"),
|
||||
sa.Column("period_start", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("period_end", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column("subscription_status_snapshot", sa.String(length=20), nullable=False),
|
||||
sa.Column("plan_code_snapshot", sa.String(length=80), nullable=False),
|
||||
sa.Column("plan_version_snapshot", sa.Integer(), nullable=False),
|
||||
sa.Column("pricing_model_snapshot", sa.String(length=24), nullable=False),
|
||||
sa.Column("billing_interval", sa.String(length=20), nullable=False),
|
||||
sa.Column("currency", sa.String(length=3), nullable=False),
|
||||
sa.Column("base_fee_snapshot", sa.Numeric(20, 4), nullable=False),
|
||||
sa.Column("seats_snapshot", sa.Integer(), nullable=False),
|
||||
sa.Column("source", sa.String(length=32), nullable=False),
|
||||
sa.Column("idempotency_key", sa.String(length=160), nullable=False),
|
||||
sa.Column("created_by", sa.String(length=120), nullable=False),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.func.now(),
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"status = 'issued'",
|
||||
name="ck_commercial_billing_periods_status",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"subscription_status_snapshot IN ('trialing', 'active', 'past_due', "
|
||||
"'suspended', 'canceled', 'expired')",
|
||||
name="ck_commercial_billing_periods_subscription_status",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"pricing_model_snapshot IN ('subscription', 'usage', 'hybrid', 'pilot', 'custom')",
|
||||
name="ck_commercial_billing_periods_pricing_model",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"billing_interval IN ('monthly', 'quarterly', 'annual', 'contract')",
|
||||
name="ck_commercial_billing_periods_interval",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"source IN ('subscription_created', 'auto_renew', 'migration_backfill')",
|
||||
name="ck_commercial_billing_periods_source",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"period_sequence >= 1 AND period_end > period_start "
|
||||
"AND plan_version_snapshot >= 1 AND base_fee_snapshot >= 0 "
|
||||
"AND seats_snapshot > 0",
|
||||
name="ck_commercial_billing_periods_values",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"length(trim(period_key)) > 0 AND length(trim(plan_code_snapshot)) > 0 "
|
||||
"AND length(trim(currency)) = 3 AND length(trim(idempotency_key)) > 0 "
|
||||
"AND length(trim(created_by)) > 0",
|
||||
name="ck_commercial_billing_periods_keys",
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tenant_id", "subscription_id"],
|
||||
["tenant_subscriptions.tenant_id", "tenant_subscriptions.id"],
|
||||
name="fk_commercial_billing_periods_tenant_subscription",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tenant_id", "plan_id"],
|
||||
["tenant_commercial_plans.tenant_id", "tenant_commercial_plans.id"],
|
||||
name="fk_commercial_billing_periods_tenant_plan",
|
||||
ondelete="RESTRICT",
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "id", name="uq_commercial_billing_periods_tenant_id"),
|
||||
sa.UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"id",
|
||||
name="uq_commercial_billing_periods_tenant_subscription_id",
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"period_sequence",
|
||||
name="uq_commercial_billing_periods_subscription_sequence",
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"period_key",
|
||||
name="uq_commercial_billing_periods_subscription_key",
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"period_start",
|
||||
name="uq_commercial_billing_periods_subscription_start",
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"tenant_id",
|
||||
"subscription_id",
|
||||
"idempotency_key",
|
||||
name="uq_commercial_billing_periods_subscription_request",
|
||||
),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_billing_periods_tenant_window",
|
||||
"commercial_billing_periods",
|
||||
["tenant_id", "period_start", "period_end"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_billing_periods_subscription_window",
|
||||
"commercial_billing_periods",
|
||||
["tenant_id", "subscription_id", "period_start", "period_end"],
|
||||
)
|
||||
|
||||
|
||||
def _create_admin_events() -> None:
|
||||
op.create_table(
|
||||
"commercial_admin_events",
|
||||
sa.Column("id", sa.String(length=36), nullable=False),
|
||||
sa.Column("tenant_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("actor_type", sa.String(length=16), nullable=False),
|
||||
sa.Column("actor_id", sa.String(length=120), nullable=False),
|
||||
sa.Column("request_id", sa.String(length=120), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=False),
|
||||
sa.Column("action", sa.String(length=48), nullable=False),
|
||||
sa.Column("resource_type", sa.String(length=24), nullable=False),
|
||||
sa.Column("resource_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("resource_version", sa.Integer(), nullable=False),
|
||||
sa.Column("before_json", sa.JSON(), nullable=False, server_default=_json_object_default()),
|
||||
sa.Column("after_json", sa.JSON(), nullable=False, server_default=_json_object_default()),
|
||||
sa.Column(
|
||||
"occurred_at",
|
||||
sa.DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=sa.func.now(),
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"actor_type IN ('user', 'system', 'migration')",
|
||||
name="ck_commercial_admin_events_actor_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"action IN ('plan_created', 'plan_activated', 'plan_retired', "
|
||||
"'subscription_created', 'subscription_activated', "
|
||||
"'subscription_transitioned', 'entitlement_created', "
|
||||
"'entitlement_updated', 'entitlement_activated', "
|
||||
"'billing_period_created', 'subscription_rolled_over', "
|
||||
"'legacy_state_imported')",
|
||||
name="ck_commercial_admin_events_action",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"resource_type IN ('plan', 'subscription', 'entitlement', 'billing_period')",
|
||||
name="ck_commercial_admin_events_resource_type",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"resource_version >= 1",
|
||||
name="ck_commercial_admin_events_resource_version",
|
||||
),
|
||||
sa.CheckConstraint(
|
||||
"length(trim(actor_id)) > 0 AND length(trim(request_id)) > 0 "
|
||||
"AND length(trim(reason)) > 0 AND length(trim(resource_id)) > 0",
|
||||
name="ck_commercial_admin_events_required_text",
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("tenant_id", "id", name="uq_commercial_admin_events_tenant_id"),
|
||||
sa.UniqueConstraint(
|
||||
"tenant_id",
|
||||
"request_id",
|
||||
"action",
|
||||
"resource_type",
|
||||
"resource_id",
|
||||
name="uq_commercial_admin_events_request_resource",
|
||||
),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_admin_events_tenant_time",
|
||||
"commercial_admin_events",
|
||||
["tenant_id", "occurred_at", "id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_admin_events_tenant_resource",
|
||||
"commercial_admin_events",
|
||||
["tenant_id", "resource_type", "resource_id", "occurred_at"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_admin_events_tenant_request",
|
||||
"commercial_admin_events",
|
||||
["tenant_id", "request_id"],
|
||||
)
|
||||
|
||||
|
||||
def _backfill_current_periods_and_audit() -> None:
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO commercial_billing_periods (
|
||||
id, tenant_id, subscription_id, plan_id, period_sequence, period_key,
|
||||
status, period_start, period_end, subscription_status_snapshot,
|
||||
plan_code_snapshot, plan_version_snapshot, pricing_model_snapshot,
|
||||
billing_interval, currency, base_fee_snapshot, seats_snapshot,
|
||||
source, idempotency_key, created_by
|
||||
)
|
||||
SELECT
|
||||
substr(digest, 1, 8) || '-' || substr(digest, 9, 4) || '-' ||
|
||||
substr(digest, 13, 4) || '-' || substr(digest, 17, 4) || '-' ||
|
||||
substr(digest, 21, 12),
|
||||
subscription.tenant_id,
|
||||
subscription.id,
|
||||
subscription.plan_id,
|
||||
1,
|
||||
'bp-' || substr(digest, 1, 24),
|
||||
'issued',
|
||||
subscription.current_period_start,
|
||||
subscription.current_period_end,
|
||||
subscription.status,
|
||||
plan.plan_code,
|
||||
plan.version,
|
||||
plan.pricing_model,
|
||||
subscription.billing_interval,
|
||||
subscription.currency,
|
||||
subscription.base_fee_snapshot,
|
||||
subscription.seats,
|
||||
'migration_backfill',
|
||||
'migration-0021:' || subscription.id,
|
||||
'migration:20260716_0021'
|
||||
FROM tenant_subscriptions AS subscription
|
||||
JOIN tenant_commercial_plans AS plan
|
||||
ON plan.tenant_id = subscription.tenant_id
|
||||
AND plan.id = subscription.plan_id
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT md5(
|
||||
subscription.tenant_id || ':' || subscription.id || ':' ||
|
||||
subscription.current_period_start::text || ':' ||
|
||||
subscription.current_period_end::text
|
||||
) AS digest
|
||||
) AS identity
|
||||
"""
|
||||
)
|
||||
for resource_type, table_name, version_column in (
|
||||
("plan", "tenant_commercial_plans", "version"),
|
||||
("subscription", "tenant_subscriptions", "version"),
|
||||
("entitlement", "commercial_entitlements", "version"),
|
||||
("billing_period", "commercial_billing_periods", "period_sequence"),
|
||||
):
|
||||
op.execute(
|
||||
f"""
|
||||
INSERT INTO commercial_admin_events (
|
||||
id, tenant_id, actor_type, actor_id, request_id, reason, action,
|
||||
resource_type, resource_id, resource_version, before_json, after_json
|
||||
)
|
||||
SELECT
|
||||
substr(digest, 1, 8) || '-' || substr(digest, 9, 4) || '-' ||
|
||||
substr(digest, 13, 4) || '-' || substr(digest, 17, 4) || '-' ||
|
||||
substr(digest, 21, 12),
|
||||
resource.tenant_id,
|
||||
'migration',
|
||||
'migration:20260716_0021',
|
||||
'migration:0021:{resource_type}:' || resource.id,
|
||||
'0021 建立商业管理审计基线,不推断迁移前操作人或原始请求。',
|
||||
'legacy_state_imported',
|
||||
'{resource_type}',
|
||||
resource.id,
|
||||
resource.{version_column},
|
||||
'{{}}'::json,
|
||||
json_build_object('id', resource.id, 'version', resource.{version_column})
|
||||
FROM {table_name} AS resource
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT md5(
|
||||
resource.tenant_id || ':audit:{resource_type}:' || resource.id
|
||||
) AS digest
|
||||
) AS identity
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _bind_usage_to_periods() -> None:
|
||||
op.add_column(
|
||||
"usage_meter_events",
|
||||
sa.Column("billing_period_id", sa.String(length=36), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"usage_meter_events",
|
||||
sa.Column("quota_period_key", sa.String(length=64), nullable=True),
|
||||
)
|
||||
op.alter_column(
|
||||
"usage_meter_events",
|
||||
"period_key",
|
||||
existing_type=sa.String(length=32),
|
||||
type_=sa.String(length=64),
|
||||
existing_nullable=False,
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE usage_meter_events AS usage
|
||||
SET billing_period_id = period.id,
|
||||
quota_period_key = usage.period_key,
|
||||
period_key = period.period_key
|
||||
FROM commercial_billing_periods AS period
|
||||
WHERE period.tenant_id = usage.tenant_id
|
||||
AND period.subscription_id = usage.subscription_id
|
||||
AND usage.occurred_at >= period.period_start
|
||||
AND usage.occurred_at < period.period_end
|
||||
"""
|
||||
)
|
||||
_fail_if_unbound(
|
||||
"usage_meter_events",
|
||||
"billing_period_id IS NULL OR quota_period_key IS NULL",
|
||||
"existing usage facts do not map to one immutable current billing period",
|
||||
)
|
||||
op.alter_column(
|
||||
"usage_meter_events", "billing_period_id", existing_type=sa.String(36), nullable=False
|
||||
)
|
||||
op.alter_column(
|
||||
"usage_meter_events", "quota_period_key", existing_type=sa.String(64), nullable=False
|
||||
)
|
||||
op.drop_constraint("ck_usage_meter_events_keys", "usage_meter_events", type_="check")
|
||||
op.create_check_constraint(
|
||||
"ck_usage_meter_events_keys",
|
||||
"usage_meter_events",
|
||||
"length(trim(metric_key)) > 0 AND length(trim(unit)) > 0 "
|
||||
"AND length(trim(period_key)) > 0 AND length(trim(source_system)) > 0 "
|
||||
"AND length(trim(quota_period_key)) > 0 "
|
||||
"AND length(trim(idempotency_key)) > 0 "
|
||||
"AND length(trim(request_fingerprint)) > 0",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_usage_meter_events_tenant_billing_period",
|
||||
"usage_meter_events",
|
||||
"commercial_billing_periods",
|
||||
["tenant_id", "subscription_id", "billing_period_id"],
|
||||
["tenant_id", "subscription_id", "id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.drop_index("ix_usage_meter_events_quota_window", table_name="usage_meter_events")
|
||||
op.create_index(
|
||||
"ix_usage_meter_events_quota_window",
|
||||
"usage_meter_events",
|
||||
["tenant_id", "subscription_id", "metric_key", "quota_period_key", "occurred_at"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_usage_meter_events_billing_period",
|
||||
"usage_meter_events",
|
||||
["tenant_id", "billing_period_id", "occurred_at"],
|
||||
)
|
||||
|
||||
|
||||
def _bind_costs_to_periods() -> None:
|
||||
op.add_column(
|
||||
"commercial_cost_events",
|
||||
sa.Column("billing_period_id", sa.String(length=36), nullable=True),
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE commercial_cost_events AS cost
|
||||
SET billing_period_id = usage.billing_period_id
|
||||
FROM usage_meter_events AS usage
|
||||
WHERE usage.tenant_id = cost.tenant_id
|
||||
AND usage.subscription_id = cost.subscription_id
|
||||
AND usage.id = cost.usage_event_id
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE commercial_cost_events AS cost
|
||||
SET billing_period_id = period.id
|
||||
FROM commercial_billing_periods AS period
|
||||
WHERE cost.subscription_id IS NOT NULL
|
||||
AND cost.billing_period_id IS NULL
|
||||
AND period.tenant_id = cost.tenant_id
|
||||
AND period.subscription_id = cost.subscription_id
|
||||
AND cost.occurred_at >= period.period_start
|
||||
AND cost.occurred_at < period.period_end
|
||||
"""
|
||||
)
|
||||
_fail_if_unbound(
|
||||
"commercial_cost_events",
|
||||
"subscription_id IS NOT NULL AND billing_period_id IS NULL",
|
||||
"existing subscription cost facts do not map to one immutable billing period",
|
||||
)
|
||||
op.create_check_constraint(
|
||||
"ck_commercial_cost_events_billing_period_pair",
|
||||
"commercial_cost_events",
|
||||
"(subscription_id IS NULL AND billing_period_id IS NULL) OR "
|
||||
"(subscription_id IS NOT NULL AND billing_period_id IS NOT NULL)",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_commercial_cost_events_tenant_billing_period",
|
||||
"commercial_cost_events",
|
||||
"commercial_billing_periods",
|
||||
["tenant_id", "subscription_id", "billing_period_id"],
|
||||
["tenant_id", "subscription_id", "id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_commercial_cost_events_subscription_period",
|
||||
table_name="commercial_cost_events",
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_cost_events_subscription_period",
|
||||
"commercial_cost_events",
|
||||
["tenant_id", "subscription_id", "billing_period_id", "occurred_at"],
|
||||
)
|
||||
|
||||
|
||||
def _bind_reservations_to_periods() -> None:
|
||||
op.add_column(
|
||||
"commercial_runtime_reservations",
|
||||
sa.Column("billing_period_id", sa.String(length=36), nullable=True),
|
||||
)
|
||||
op.add_column(
|
||||
"commercial_runtime_reservations",
|
||||
sa.Column("quota_period_key", sa.String(length=64), nullable=True),
|
||||
)
|
||||
op.alter_column(
|
||||
"commercial_runtime_reservations",
|
||||
"period_key",
|
||||
existing_type=sa.String(length=32),
|
||||
type_=sa.String(length=64),
|
||||
existing_nullable=False,
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE commercial_runtime_reservations AS reservation
|
||||
SET billing_period_id = period.id,
|
||||
quota_period_key = reservation.period_key,
|
||||
period_key = period.period_key
|
||||
FROM commercial_billing_periods AS period
|
||||
WHERE period.tenant_id = reservation.tenant_id
|
||||
AND period.subscription_id = reservation.subscription_id
|
||||
AND reservation.created_at >= period.period_start
|
||||
AND reservation.created_at < period.period_end
|
||||
"""
|
||||
)
|
||||
_fail_if_unbound(
|
||||
"commercial_runtime_reservations",
|
||||
"billing_period_id IS NULL OR quota_period_key IS NULL",
|
||||
"existing runtime reservations do not map to one immutable billing period",
|
||||
)
|
||||
op.alter_column(
|
||||
"commercial_runtime_reservations",
|
||||
"billing_period_id",
|
||||
existing_type=sa.String(36),
|
||||
nullable=False,
|
||||
)
|
||||
op.alter_column(
|
||||
"commercial_runtime_reservations",
|
||||
"quota_period_key",
|
||||
existing_type=sa.String(64),
|
||||
nullable=False,
|
||||
)
|
||||
op.drop_constraint(
|
||||
"ck_commercial_runtime_reservations_keys",
|
||||
"commercial_runtime_reservations",
|
||||
type_="check",
|
||||
)
|
||||
op.create_check_constraint(
|
||||
"ck_commercial_runtime_reservations_keys",
|
||||
"commercial_runtime_reservations",
|
||||
"length(trim(run_id)) > 0 AND length(trim(tool_call_id)) > 0 "
|
||||
"AND length(trim(tool_type)) > 0 AND length(trim(tool_name)) > 0 "
|
||||
"AND length(trim(period_key)) > 0 AND length(trim(quota_period_key)) > 0 "
|
||||
"AND length(trim(request_fingerprint)) > 0",
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"fk_commercial_runtime_reservations_tenant_billing_period",
|
||||
"commercial_runtime_reservations",
|
||||
"commercial_billing_periods",
|
||||
["tenant_id", "subscription_id", "billing_period_id"],
|
||||
["tenant_id", "subscription_id", "id"],
|
||||
ondelete="RESTRICT",
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_commercial_runtime_reservations_quota",
|
||||
table_name="commercial_runtime_reservations",
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_runtime_reservations_quota",
|
||||
"commercial_runtime_reservations",
|
||||
["tenant_id", "subscription_id", "entitlement_id", "quota_period_key", "status"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_runtime_reservations_billing_period",
|
||||
"commercial_runtime_reservations",
|
||||
["tenant_id", "billing_period_id", "status"],
|
||||
)
|
||||
|
||||
|
||||
def _fail_if_unbound(table_name: str, predicate: str, message: str) -> None:
|
||||
op.execute(
|
||||
f"""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM {table_name} WHERE {predicate}) THEN
|
||||
RAISE EXCEPTION '{message}';
|
||||
END IF;
|
||||
END;
|
||||
$$
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _create_append_only_triggers() -> None:
|
||||
for table_name in ("commercial_billing_periods", "commercial_admin_events"):
|
||||
op.execute(
|
||||
f"""
|
||||
CREATE TRIGGER trg_{table_name}_append_only
|
||||
BEFORE UPDATE OR DELETE ON {table_name}
|
||||
FOR EACH ROW EXECUTE FUNCTION prevent_commercial_events_mutation()
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _create_period_overlap_guard() -> None:
|
||||
op.execute(
|
||||
"""
|
||||
CREATE FUNCTION prevent_commercial_billing_period_overlap()
|
||||
RETURNS trigger AS $$
|
||||
BEGIN
|
||||
PERFORM pg_advisory_xact_lock(
|
||||
hashtextextended(NEW.tenant_id || ':' || NEW.subscription_id, 0)
|
||||
);
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM commercial_billing_periods AS existing
|
||||
WHERE existing.tenant_id = NEW.tenant_id
|
||||
AND existing.subscription_id = NEW.subscription_id
|
||||
AND tstzrange(
|
||||
existing.period_start,
|
||||
existing.period_end,
|
||||
'[)'
|
||||
) && tstzrange(NEW.period_start, NEW.period_end, '[)')
|
||||
) THEN
|
||||
RAISE EXCEPTION 'commercial billing period overlaps an issued period';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
CREATE TRIGGER trg_commercial_billing_periods_no_overlap
|
||||
BEFORE INSERT ON commercial_billing_periods
|
||||
FOR EACH ROW EXECUTE FUNCTION prevent_commercial_billing_period_overlap()
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
_require_postgresql()
|
||||
_require_empty_billing_lifecycle_for_downgrade()
|
||||
_unbind_reservations()
|
||||
_unbind_costs()
|
||||
_unbind_usage()
|
||||
op.execute(
|
||||
"DROP TRIGGER IF EXISTS trg_commercial_billing_periods_no_overlap "
|
||||
"ON commercial_billing_periods"
|
||||
)
|
||||
for table_name in ("commercial_admin_events", "commercial_billing_periods"):
|
||||
op.execute(f"DROP TRIGGER IF EXISTS trg_{table_name}_append_only ON {table_name}")
|
||||
op.drop_table("commercial_admin_events")
|
||||
op.drop_table("commercial_billing_periods")
|
||||
op.execute("DROP FUNCTION IF EXISTS prevent_commercial_billing_period_overlap()")
|
||||
|
||||
|
||||
def _unbind_reservations() -> None:
|
||||
op.drop_index(
|
||||
"ix_commercial_runtime_reservations_billing_period",
|
||||
table_name="commercial_runtime_reservations",
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_commercial_runtime_reservations_quota",
|
||||
table_name="commercial_runtime_reservations",
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_runtime_reservations_quota",
|
||||
"commercial_runtime_reservations",
|
||||
["tenant_id", "subscription_id", "entitlement_id", "period_key", "status"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_commercial_runtime_reservations_tenant_billing_period",
|
||||
"commercial_runtime_reservations",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"ck_commercial_runtime_reservations_keys",
|
||||
"commercial_runtime_reservations",
|
||||
type_="check",
|
||||
)
|
||||
op.create_check_constraint(
|
||||
"ck_commercial_runtime_reservations_keys",
|
||||
"commercial_runtime_reservations",
|
||||
"length(trim(run_id)) > 0 AND length(trim(tool_call_id)) > 0 "
|
||||
"AND length(trim(tool_type)) > 0 AND length(trim(tool_name)) > 0 "
|
||||
"AND length(trim(period_key)) > 0 "
|
||||
"AND length(trim(request_fingerprint)) > 0",
|
||||
)
|
||||
op.drop_column("commercial_runtime_reservations", "quota_period_key")
|
||||
op.drop_column("commercial_runtime_reservations", "billing_period_id")
|
||||
op.alter_column(
|
||||
"commercial_runtime_reservations",
|
||||
"period_key",
|
||||
existing_type=sa.String(length=64),
|
||||
type_=sa.String(length=32),
|
||||
existing_nullable=False,
|
||||
)
|
||||
|
||||
|
||||
def _unbind_costs() -> None:
|
||||
op.drop_index(
|
||||
"ix_commercial_cost_events_subscription_period",
|
||||
table_name="commercial_cost_events",
|
||||
)
|
||||
op.create_index(
|
||||
"ix_commercial_cost_events_subscription_period",
|
||||
"commercial_cost_events",
|
||||
["tenant_id", "subscription_id", "occurred_at"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_commercial_cost_events_tenant_billing_period",
|
||||
"commercial_cost_events",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint(
|
||||
"ck_commercial_cost_events_billing_period_pair",
|
||||
"commercial_cost_events",
|
||||
type_="check",
|
||||
)
|
||||
op.drop_column("commercial_cost_events", "billing_period_id")
|
||||
|
||||
|
||||
def _unbind_usage() -> None:
|
||||
op.drop_index("ix_usage_meter_events_billing_period", table_name="usage_meter_events")
|
||||
op.drop_index("ix_usage_meter_events_quota_window", table_name="usage_meter_events")
|
||||
op.create_index(
|
||||
"ix_usage_meter_events_quota_window",
|
||||
"usage_meter_events",
|
||||
["tenant_id", "subscription_id", "metric_key", "period_key", "occurred_at"],
|
||||
)
|
||||
op.drop_constraint(
|
||||
"fk_usage_meter_events_tenant_billing_period",
|
||||
"usage_meter_events",
|
||||
type_="foreignkey",
|
||||
)
|
||||
op.drop_constraint("ck_usage_meter_events_keys", "usage_meter_events", type_="check")
|
||||
op.create_check_constraint(
|
||||
"ck_usage_meter_events_keys",
|
||||
"usage_meter_events",
|
||||
"length(trim(metric_key)) > 0 AND length(trim(unit)) > 0 "
|
||||
"AND length(trim(period_key)) > 0 AND length(trim(source_system)) > 0 "
|
||||
"AND length(trim(idempotency_key)) > 0 "
|
||||
"AND length(trim(request_fingerprint)) > 0",
|
||||
)
|
||||
op.drop_column("usage_meter_events", "quota_period_key")
|
||||
op.drop_column("usage_meter_events", "billing_period_id")
|
||||
op.alter_column(
|
||||
"usage_meter_events",
|
||||
"period_key",
|
||||
existing_type=sa.String(length=64),
|
||||
type_=sa.String(length=32),
|
||||
existing_nullable=False,
|
||||
)
|
||||
Reference in New Issue
Block a user