Skip to content

fix(ledger): add internal foreign keys#4652

Open
GAlexIHU wants to merge 1 commit into
mainfrom
codex/ledger-data-layer-fks
Open

fix(ledger): add internal foreign keys#4652
GAlexIHU wants to merge 1 commit into
mainfrom
codex/ledger-data-layer-fks

Conversation

@GAlexIHU

@GAlexIHU GAlexIHU commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add ledger-internal foreign keys for ledger_customer_accounts.account_id and ledger_breakage_records ledger references.
  • Keep external ids such as customer and charge ids as soft references.
  • Regenerate Ent code and add the Atlas migration with the new FK constraints marked NOT VALID for deploy safety.

Deploy Callout

After the migration has been applied, run the manual validation commands below after orphan preflight is clean:

SET lock_timeout = '5s';
SET statement_timeout = '0';

ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_breakage_records_planned_release;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_breakage_records_release_reopens;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_entries_source_breakage_records;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_sub_accounts_breakage_records;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_sub_accounts_fbo_breakage_record;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_transaction_groups_breakage_reco;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_transaction_groups_source_breaka;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_transactions_breakage_records;
ALTER TABLE ledger_breakage_records VALIDATE CONSTRAINT ledger_breakage_records_ledger_transactions_source_breakage_rec;

ALTER TABLE ledger_customer_accounts VALIDATE CONSTRAINT ledger_customer_accounts_ledger_accounts_customer_accounts;

Validation

  • nix develop --impure .#ci -c atlas migrate --env local hash
  • nix develop --impure .#ci -c atlas migrate --env local diff ledger_internal_fks_check
  • nix develop --impure .#ci -c make generate
  • nix develop --impure .#ci -c go vet -tags=dynamic ./openmeter/ledger/...
  • nix develop --impure .#ci -c env POSTGRES_HOST=127.0.0.1 go test -tags=dynamic ./openmeter/ledger/...

Summary by CodeRabbit

  • New Features

    • Added new ledger relationships to support breakage records, customer accounts, and source references across transactions, transaction groups, entries, and sub-accounts.
    • Improved breakage record handling with support for planned releases and release reopens.
  • Bug Fixes

    • Strengthened deletion safeguards so related ledger records can’t be removed when they’re still referenced.
    • Updated customer account identifiers to use a fixed-length format for consistency.
  • Tests

    • Expanded breakage-related test coverage with more realistic ledger data scenarios.

@GAlexIHU GAlexIHU added the release-note/ignore Ignore this change when generating release notes label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds ent schema edges linking LedgerBreakageRecord to LedgerAccount, LedgerSubAccount, LedgerEntry, LedgerTransaction, LedgerTransactionGroup, and itself (with Restrict delete rules), adds a LedgerCustomerAccount → LedgerAccount edge with a typed account_id column, includes matching up/down SQL migrations, and updates a breakage adapter test helper accordingly.

Changes

Ledger internal FK wiring

Layer / File(s) Summary
LedgerBreakageRecord edges and related entity wiring
openmeter/ent/schema/ledger_breakage_record.go, ledger_account.go, ledger_entry.go, ledger_transaction.go, ledger_transaction_group.go
Defines LedgerBreakageRecord's foreign-key-backed edges (source/breakage transaction group, transaction, entry, sub-account references, self-referencing planned_releases/release_reopens) with Restrict delete rules, and wires reciprocal edges from LedgerAccount, LedgerSubAccount, LedgerEntry, LedgerTransaction, and LedgerTransactionGroup.
LedgerCustomerAccount account_id typing and edge
openmeter/ent/schema/ledger_customer_account.go
Changes account_id to a Postgres char(26) schema type and adds a required, immutable, unique edge to LedgerAccount.
SQL migration for ledger foreign keys
tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql, ...down.sql
Adds NOT VALID foreign key constraints across ledger_breakage_records and ledger_customer_accounts (plus account_id column type change) in the up migration, with matching drops/reverts in the down migration.
Breakage adapter test helper update
openmeter/ledger/breakage/adapter/record_test.go
Rewrites newExpiredRecord to commit a historical ledger transaction group and derive sub-account/transaction IDs, expanding test fixtures with extra feature-route and currency cases.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: turip, borosr

Alright, this one's a nice, tidy schema-and-migration bundle — new edges tying breakage records into the rest of the ledger graph, a fresh typed column, and a test helper that now does the heavy lifting itself. Should be a smooth read through! 🧵

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding internal foreign keys in the ledger schemas and migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/ledger-data-layer-fks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@GAlexIHU GAlexIHU marked this pull request as ready for review July 6, 2026 14:41
@GAlexIHU GAlexIHU requested a review from a team as a code owner July 6, 2026 14:41
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds ledger-internal foreign keys for ledger account and breakage records. The main changes are:

  • New Ent edges between breakage records and ledger transactions, entries, subaccounts, and related records.
  • New Ent edge from customer account mappings to ledger accounts.
  • Generated Ent code for the new relationships.
  • Atlas migration adding NOT VALID foreign-key constraints.

Confidence Score: 4/5

The migration path needs a data-shape fix before merging.

  • The new Ent relationships match the main application write paths.
  • The customer account migration can still fail on existing over-length account_id values.
  • Legacy orphaned customer-account mappings can fail later row updates after the FK is added.

tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql

Important Files Changed

Filename Overview
openmeter/ent/schema/ledger_breakage_record.go Adds Ent relationships from breakage records to ledger transaction groups, transactions, entries, subaccounts, and self-referential plan/release records.
openmeter/ent/schema/ledger_customer_account.go Changes account_id to Postgres char(26) and models it as a required ledger account relationship.
tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql Adds NOT VALID foreign keys, while also narrowing ledger_customer_accounts.account_id from unconstrained varchar to character(26).
openmeter/ledger/breakage/adapter/record_test.go Updates the breakage record test helper to create real ledger transaction and subaccount references.

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql:6
**Column Narrowing Blocks Migration**

When an existing `ledger_customer_accounts` row has an `account_id` longer than 26 characters, this `ALTER COLUMN` fails before the `NOT VALID` foreign key can defer historical cleanup. The original column was unconstrained `character varying`, so a deploy with legacy or manually imported data can stop at this migration even if the FK orphan preflight is clean.

### Issue 2 of 2
tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql:6
**Legacy Orphans Fail Updates**

`NOT VALID` lets old `ledger_customer_accounts.account_id` values skip the initial FK scan, but PostgreSQL still checks the constraint on later updates to those rows. If an existing mapping points at no `ledger_accounts` row, a routine update such as soft-delete or metadata maintenance can start failing with a foreign-key violation after this migration.

Reviews (1): Last reviewed commit: "fix(ledger): add internal foreign keys" | Re-trigger Greptile

ALTER TABLE "ledger_breakage_records" ADD CONSTRAINT "ledger_breakage_records_ledger_breakage_records_planned_release" FOREIGN KEY ("plan_id") REFERENCES "ledger_breakage_records" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_breakage_records_release_reopens" FOREIGN KEY ("release_id") REFERENCES "ledger_breakage_records" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_entries_source_breakage_records" FOREIGN KEY ("source_entry_id") REFERENCES "ledger_entries" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_sub_accounts_breakage_records" FOREIGN KEY ("breakage_sub_account_id") REFERENCES "ledger_sub_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_sub_accounts_fbo_breakage_record" FOREIGN KEY ("fbo_sub_account_id") REFERENCES "ledger_sub_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transaction_groups_breakage_reco" FOREIGN KEY ("breakage_transaction_group_id") REFERENCES "ledger_transaction_groups" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transaction_groups_source_breaka" FOREIGN KEY ("source_transaction_group_id") REFERENCES "ledger_transaction_groups" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transactions_breakage_records" FOREIGN KEY ("breakage_transaction_id") REFERENCES "ledger_transactions" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transactions_source_breakage_rec" FOREIGN KEY ("source_transaction_id") REFERENCES "ledger_transactions" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID;
-- modify "ledger_customer_accounts" table
-- Manual post-generation step: keep this foreign key NOT VALID for the same rollout reason as above.
ALTER TABLE "ledger_customer_accounts" ALTER COLUMN "account_id" TYPE character(26), ADD CONSTRAINT "ledger_customer_accounts_ledger_accounts_customer_accounts" FOREIGN KEY ("account_id") REFERENCES "ledger_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Column Narrowing Blocks Migration

When an existing ledger_customer_accounts row has an account_id longer than 26 characters, this ALTER COLUMN fails before the NOT VALID foreign key can defer historical cleanup. The original column was unconstrained character varying, so a deploy with legacy or manually imported data can stop at this migration even if the FK orphan preflight is clean.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql
Line: 6

Comment:
**Column Narrowing Blocks Migration**

When an existing `ledger_customer_accounts` row has an `account_id` longer than 26 characters, this `ALTER COLUMN` fails before the `NOT VALID` foreign key can defer historical cleanup. The original column was unconstrained `character varying`, so a deploy with legacy or manually imported data can stop at this migration even if the FK orphan preflight is clean.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

ALTER TABLE "ledger_breakage_records" ADD CONSTRAINT "ledger_breakage_records_ledger_breakage_records_planned_release" FOREIGN KEY ("plan_id") REFERENCES "ledger_breakage_records" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_breakage_records_release_reopens" FOREIGN KEY ("release_id") REFERENCES "ledger_breakage_records" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_entries_source_breakage_records" FOREIGN KEY ("source_entry_id") REFERENCES "ledger_entries" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_sub_accounts_breakage_records" FOREIGN KEY ("breakage_sub_account_id") REFERENCES "ledger_sub_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_sub_accounts_fbo_breakage_record" FOREIGN KEY ("fbo_sub_account_id") REFERENCES "ledger_sub_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transaction_groups_breakage_reco" FOREIGN KEY ("breakage_transaction_group_id") REFERENCES "ledger_transaction_groups" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transaction_groups_source_breaka" FOREIGN KEY ("source_transaction_group_id") REFERENCES "ledger_transaction_groups" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transactions_breakage_records" FOREIGN KEY ("breakage_transaction_id") REFERENCES "ledger_transactions" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID, ADD CONSTRAINT "ledger_breakage_records_ledger_transactions_source_breakage_rec" FOREIGN KEY ("source_transaction_id") REFERENCES "ledger_transactions" ("id") ON UPDATE NO ACTION ON DELETE RESTRICT NOT VALID;
-- modify "ledger_customer_accounts" table
-- Manual post-generation step: keep this foreign key NOT VALID for the same rollout reason as above.
ALTER TABLE "ledger_customer_accounts" ALTER COLUMN "account_id" TYPE character(26), ADD CONSTRAINT "ledger_customer_accounts_ledger_accounts_customer_accounts" FOREIGN KEY ("account_id") REFERENCES "ledger_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Legacy Orphans Fail Updates

NOT VALID lets old ledger_customer_accounts.account_id values skip the initial FK scan, but PostgreSQL still checks the constraint on later updates to those rows. If an existing mapping points at no ledger_accounts row, a routine update such as soft-delete or metadata maintenance can start failing with a foreign-key violation after this migration.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql
Line: 6

Comment:
**Legacy Orphans Fail Updates**

`NOT VALID` lets old `ledger_customer_accounts.account_id` values skip the initial FK scan, but PostgreSQL still checks the constraint on later updates to those rows. If an existing mapping points at no `ledger_accounts` row, a routine update such as soft-delete or metadata maintenance can start failing with a foreign-key violation after this migration.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openmeter/ent/schema/ledger_breakage_record.go`:
- Around line 130-187: The LedgerBreakageRecord schema is still missing indexes
on several foreign key fields, causing parent-table deletes to scan this table.
Update the Edges definition in LedgerBreakageRecord to ensure the remaining
FK-backed edges, especially source_transaction, breakage_transaction,
fbo_sub_account, breakage_sub_account, and release, are indexed in the generated
schema by adding the appropriate index annotations or schema indexes tied to
those field names.

In `@tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql`:
- Around line 4-6: The combined ALTER TABLE on ledger_customer_accounts is still
doing a blocking column type rewrite, so separate the account_id type change
from the foreign key addition. Update the migration around the
ledger_customer_accounts ALTER TABLE statement so the TYPE character(26) change
is its own step, then add the
ledger_customer_accounts_ledger_accounts_customer_accounts foreign key in a
separate NOT VALID statement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 54c3c901-c48c-4c59-bb21-c21a8e2da563

📥 Commits

Reviewing files that changed from the base of the PR and between ef77b70 and 08abedb.

⛔ Files ignored due to path filters (46)
  • openmeter/ent/db/client.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgeraccount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgeraccount/ledgeraccount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgeraccount/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgeraccount_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgeraccount_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgeraccount_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerbreakagerecord.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerbreakagerecord/ledgerbreakagerecord.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerbreakagerecord/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerbreakagerecord_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerbreakagerecord_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerbreakagerecord_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgercustomeraccount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgercustomeraccount/ledgercustomeraccount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgercustomeraccount/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgercustomeraccount_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgercustomeraccount_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgercustomeraccount_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerentry.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerentry/ledgerentry.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerentry/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerentry_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerentry_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgerentry_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgersubaccount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgersubaccount/ledgersubaccount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgersubaccount/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgersubaccount_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgersubaccount_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgersubaccount_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransaction.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransaction/ledgertransaction.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransaction/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransaction_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransaction_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransaction_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransactiongroup.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransactiongroup/ledgertransactiongroup.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransactiongroup/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransactiongroup_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransactiongroup_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ledgertransactiongroup_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (9)
  • openmeter/ent/schema/ledger_account.go
  • openmeter/ent/schema/ledger_breakage_record.go
  • openmeter/ent/schema/ledger_customer_account.go
  • openmeter/ent/schema/ledger_entry.go
  • openmeter/ent/schema/ledger_transaction.go
  • openmeter/ent/schema/ledger_transaction_group.go
  • openmeter/ledger/breakage/adapter/record_test.go
  • tools/migrate/migrations/20260706130134_add_ledger_internal_fks.down.sql
  • tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql

Comment on lines +130 to +187
func (LedgerBreakageRecord) Edges() []ent.Edge {
return []ent.Edge{
edge.From("source_transaction_group", LedgerTransactionGroup.Type).
Ref("source_breakage_records").
Field("source_transaction_group_id").
Immutable().
Unique(),
edge.From("source_transaction", LedgerTransaction.Type).
Ref("source_breakage_records").
Field("source_transaction_id").
Immutable().
Unique(),
edge.From("source_entry", LedgerEntry.Type).
Ref("source_breakage_records").
Field("source_entry_id").
Immutable().
Unique(),
edge.From("breakage_transaction_group", LedgerTransactionGroup.Type).
Ref("breakage_records").
Field("breakage_transaction_group_id").
Required().
Immutable().
Unique(),
edge.From("breakage_transaction", LedgerTransaction.Type).
Ref("breakage_records").
Field("breakage_transaction_id").
Required().
Immutable().
Unique(),
edge.From("fbo_sub_account", LedgerSubAccount.Type).
Ref("fbo_breakage_records").
Field("fbo_sub_account_id").
Required().
Immutable().
Unique(),
edge.From("breakage_sub_account", LedgerSubAccount.Type).
Ref("breakage_records").
Field("breakage_sub_account_id").
Required().
Immutable().
Unique(),
edge.To("planned_releases", LedgerBreakageRecord.Type).
Annotations(entsql.OnDelete(entsql.Restrict)),
edge.To("release_reopens", LedgerBreakageRecord.Type).
Annotations(entsql.OnDelete(entsql.Restrict)),
edge.From("plan", LedgerBreakageRecord.Type).
Ref("planned_releases").
Field("plan_id").
Immutable().
Unique(),
edge.From("release", LedgerBreakageRecord.Type).
Ref("release_reopens").
Field("release_id").
Immutable().
Unique(),
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for index declarations covering the FK columns backing the new edges.
fd -e go . openmeter/ent/schema --exec rg -n 'index\.Fields\(' {} \; 
echo "---- breakage record indexes ----"
rg -nP 'index\.Fields\([^)]*(source_transaction_id|breakage_transaction_id|fbo_sub_account_id|breakage_sub_account_id|release_id)' openmeter/ent/schema/ledger_breakage_record.go || echo "no matching FK-column indexes found in schema"

Repository: openmeterio/openmeter

Length of output: 8239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== schema outline =="
ast-grep outline openmeter/ent/schema/ledger_breakage_record.go --view expanded || true

echo
echo "== relevant schema sections =="
sed -n '1,260p' openmeter/ent/schema/ledger_breakage_record.go

echo
echo "== generated references for ledger_breakage_records =="
rg -n "ledger_breakage_records|source_transaction_id|breakage_transaction_id|fbo_sub_account_id|breakage_sub_account_id|release_id|plan_id|source_transaction_group_id|source_entry_id|breakage_transaction_group_id" openmeter/ent -g '!**/*.json' -g '!**/*.snap' || true

Repository: openmeterio/openmeter

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the generated schema for this entity if it exists.
fd -a 'ledger_breakage_record.go' openmeter/ent | sed -n '1,20p'
echo
for f in $(fd -a 'ledger_breakage_record.go' openmeter/ent); do
  echo "== $f =="
  rg -n "func \(.*\) Indexes\(\)|index\.Fields|Unique\(|source_transaction_id|breakage_transaction_id|fbo_sub_account_id|breakage_sub_account_id|release_id|plan_id|source_transaction_group_id|source_entry_id|breakage_transaction_group_id" "$f" || true
  echo
done

Repository: openmeterio/openmeter

Length of output: 1605


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '3695,3810p' openmeter/ent/db/migrate/schema.go

Repository: openmeterio/openmeter

Length of output: 4879


Add indexes for the remaining breakage FK columns. source_transaction_id, breakage_transaction_id, fbo_sub_account_id, breakage_sub_account_id, and release_id still don't get indexes in the generated ledger_breakage_records schema, so deletes on the parent tables can end up scanning this table.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openmeter/ent/schema/ledger_breakage_record.go` around lines 130 - 187, The
LedgerBreakageRecord schema is still missing indexes on several foreign key
fields, causing parent-table deletes to scan this table. Update the Edges
definition in LedgerBreakageRecord to ensure the remaining FK-backed edges,
especially source_transaction, breakage_transaction, fbo_sub_account,
breakage_sub_account, and release, are indexed in the generated schema by adding
the appropriate index annotations or schema indexes tied to those field names.

Source: Path instructions

Comment on lines +4 to +6
-- modify "ledger_customer_accounts" table
-- Manual post-generation step: keep this foreign key NOT VALID for the same rollout reason as above.
ALTER TABLE "ledger_customer_accounts" ALTER COLUMN "account_id" TYPE character(26), ADD CONSTRAINT "ledger_customer_accounts_ledger_accounts_customer_accounts" FOREIGN KEY ("account_id") REFERENCES "ledger_accounts" ("id") ON UPDATE NO ACTION ON DELETE NO ACTION NOT VALID;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

Does PostgreSQL treat ALTER COLUMN TYPE from varchar to char(n) as a binary-coercible change that avoids a table rewrite?

💡 Result:

PostgreSQL does not treat an ALTER COLUMN TYPE from varchar to char(n) as a binary-coercible change, and it generally requires a table rewrite [1][2][3]. While PostgreSQL can avoid a full table rewrite when changing between certain types that are binary-coercible or are unconstrained domains over the new type (such as changing from varchar(n) to text or increasing the length of a varchar(n)), the conversion between varchar and char(n) is not considered binary-coercible [1][4]. The char(n) type requires that data be padded with spaces to the specified length n, whereas varchar stores only the string itself. Because the internal on-disk representation differs, PostgreSQL must rewrite the table to convert the values to the fixed-length, space-padded format required by char(n) [5][4]. This operation holds an ACCESS EXCLUSIVE lock on the table for the duration of the rewrite, which can cause significant downtime on large tables [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql'

echo '--- file ---'
cat -n "$file"

echo
echo '--- surrounding migration files ---'
fd -a '20260706130134_add_ledger_internal_fks.*\.sql$|20260706130134_add_ledger_internal_fks\.up\.sql|20260706130134_add_ledger_internal_fks\.down\.sql' tools/migrate/migrations || true

Repository: openmeterio/openmeter

Length of output: 3004


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql'
wc -l "$file"
sed -n '1,40p' "$file"

Repository: openmeterio/openmeter

Length of output: 2803


Split the type change out from the FK add

ALTER COLUMN "account_id" TYPE character(26) still rewrites ledger_customer_accounts under an ACCESS EXCLUSIVE lock, so the NOT VALID FK doesn’t buy you the low-lock rollout benefit here. Split the type change into its own step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/migrate/migrations/20260706130134_add_ledger_internal_fks.up.sql`
around lines 4 - 6, The combined ALTER TABLE on ledger_customer_accounts is
still doing a blocking column type rewrite, so separate the account_id type
change from the foreign key addition. Update the migration around the
ledger_customer_accounts ALTER TABLE statement so the TYPE character(26) change
is its own step, then add the
ledger_customer_accounts_ledger_accounts_customer_accounts foreign key in a
separate NOT VALID statement.

Source: Path instructions

@GAlexIHU GAlexIHU enabled auto-merge (squash) July 6, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant