62 lines
2.2 KiB
Python
62 lines
2.2 KiB
Python
"""D2 — a credit limit stops being rounded to a whole number
|
|
|
|
`chat_credit_quota.daily_credit_limit` is `bigint` in the database and
|
|
`DECIMAL(12, 2)` on the model. PostgreSQL resolves that disagreement by
|
|
rounding, silently, on write:
|
|
|
|
INSERT ... VALUES (0.60::numeric(12,2)) -> stored: 1
|
|
INSERT ... VALUES (10.75::numeric(12,2)) -> stored: 11
|
|
INSERT ... VALUES (10.25::numeric(12,2)) -> stored: 10
|
|
|
|
So a tenant admin setting a limit of 0.60 credits gets 1 — a 67% overshoot — and
|
|
nothing anywhere reports it. Whether that has bitten depends on whether anyone
|
|
has entered a fraction, which is not a property worth relying on.
|
|
|
|
Widening, so no value is at risk: every bigint currently stored is representable
|
|
in `numeric(12, 2)`. `credits_used_today` and `total_credits_ever` are left as
|
|
`bigint` deliberately — they count tokens, which are whole.
|
|
|
|
The reverse casts back to `bigint` and therefore *does* round. That is the
|
|
honest reverse of a widening: going back to a narrower type cannot preserve what
|
|
the wider one held. It is written that way rather than left unimplemented so the
|
|
migration can actually be reversed, but rolling this back after fractional
|
|
limits have been set will round them.
|
|
|
|
Found by `alembic revision --autogenerate`, which had never been run against
|
|
this schema, and which after the model-registry fix reports this as the single
|
|
remaining difference between the models and the database.
|
|
|
|
Revision ID: d2_0_credit_limit_precision
|
|
Revises: d1_0_plans_and_entitlements
|
|
"""
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision = "d2_0_credit_limit_precision"
|
|
down_revision = "d1_0_plans_and_entitlements"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.alter_column(
|
|
"chat_credit_quota",
|
|
"daily_credit_limit",
|
|
existing_type=sa.BigInteger(),
|
|
type_=sa.DECIMAL(precision=12, scale=2),
|
|
existing_nullable=False,
|
|
postgresql_using="daily_credit_limit::numeric(12,2)",
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.alter_column(
|
|
"chat_credit_quota",
|
|
"daily_credit_limit",
|
|
existing_type=sa.DECIMAL(precision=12, scale=2),
|
|
type_=sa.BigInteger(),
|
|
existing_nullable=False,
|
|
postgresql_using="round(daily_credit_limit)::bigint",
|
|
)
|