feat(migrations): add container virtual upstream rules schema (S32 plan: 3/19)
What this does
Adds the container_virtual_upstream_rules schema. The table stores allow and deny rules for a container virtual upstream association.
The migration defines 64 namespace hash partitions, the UUIDv7 primary key, the required checks, foreign keys, and the rules lookup index. It also regenerates the schema snapshot and Jet bindings.
Decisions a reviewer should check first
- The association foreign key uses
ON DELETE CASCADE. A repository delete removes the association and its rules as one subtree. - The namespace foreign key uses
ON DELETE NO ACTION. A namespace delete refuses while rule rows exist. - The lookup index is non-partial. The batched rules read has no predicate that can use a partial index.
- The migration uses
NO TRANSACTION. Each partition statement releases its locks before the next partition statement.
Spec coverage
| # | Criterion | Tests |
|---|---|---|
| AC-Schema-1 | Upstream delete is refused and the referencing rows survive. | TestContainerVirtualRulesConstraints_DeleteActions/listed_upstream_delete_leaves_rules_intact |
| AC-Schema-2 | Deleting the virtual repository removes its whole subtree, rules included. | TestContainerVirtualRulesConstraints_DeleteActions/repositories_delete_cascades_three_levels, TestContainerVirtualRulesConstraints_DeleteActions/virtual_repository_delete_cascades_two_levels, TestContainerVirtualRulesConstraints_DeleteActions/association_delete_cascades |
| AC-Schema-3 | Namespace isolation: the composite FK rejects a cross-namespace reference. | TestContainerVirtualRulesConstraints_RejectsAbsentReferences/cross_namespace_association |
| AC-Schema-4 | Double association is impossible. | Owned by container_virtual_repository_upstreams. Not this table. |
| AC-Schema-5 | Reordering is transactional and the deferral is what makes it work. | Owned by container_virtual_repository_upstreams. This table declares no deferrable constraint: TestContainerVirtualRulesSchema_NothingIsDeferrable |
| AC-Schema-6 | Range constraints bind: rule_type outside (0, 1), target_field outside (0, 1), and a pattern over 255 characters are each refused. | TestContainerVirtualRulesConstraints_RejectsOutOfRangeValues, TestContainerVirtualRulesConstraints_AcceptsEveryInRangeValue |
| AC-Rules-corrupt-target-field | A corrupt target_field fails closed on the requests that do not address it. | Read-path criterion, owned by the resolution steps. The write-side half is the CHECK: TestContainerVirtualRulesConstraints_RejectsOutOfRangeValues/target_field_above_the_range |
| AC-Rules-corrupt-rule | A corrupt rule answers 500. | Read-path criterion, owned by the resolution steps. The write-side half is the CHECK: TestContainerVirtualRulesConstraints_RejectsOutOfRangeValues |
| AC-Resolution-, AC-Probe-, AC-Rules-* (evaluation), AC-Method-, AC-Concurrency- | Resolution, probe, rule evaluation, method discipline, and concurrency. | Owned by later steps. No schema surface in this MR. |
| Requirement | Tests |
|---|---|
| 64 HASH partitions, primary key, columns and nullability | TestContainerVirtualRulesSchema_PartitionedTableAndColumns |
| Partition routing by hash of namespace_id | TestContainerVirtualRulesSchema_PartitionRoutingByHashOfNamespaceID |
| The batched read's index, its column order, and its leaf clones | TestContainerVirtualRulesSchema_KeysAndIndexes |
| Both FK actions and the CHECK count | TestContainerVirtualRulesSchema_ForeignKeysAndChecks |
| Each CHECK's rendered definition and reported name | TestContainerVirtualRulesSchema_CheckShapes |
| No deferrable constraint, and an immediate primary-key index on every leaf | TestContainerVirtualRulesSchema_NothingIsDeferrable |
| Constraint-name arithmetic | TestContainerVirtualRulesSchema_ConstraintNamesFitIdentifierLimit |
| Migration apply and rollback structure, and the bracketed lock and statement budgets | TestContainerVirtualRulesSchema_MigrationShape |
| Every Up statement guarded, and none an ADD CONSTRAINT | TestContainerVirtualRulesSchema_UpIsReplayable |
| The migration applies after the table its foreign key targets | TestContainerVirtualRulesSchema_AppliesAfterTheAssociationTable |
No e2e scenario changes apply. This step adds only database schema. No endpoint is reachable from this change.
Size
This MR has 4,312 reviewable LOC. The schema snapshot adds 1,625 LOC. The migration adds 668 LOC. The schema integration test adds 1,227 LOC. The row-shape integration test adds 675 LOC. Generated Jet bindings add 115 LOC. The remaining migration guards add 2 LOC. These artifacts are one schema change and must land together.
Checks
- Full pre-commit hook suite passed during commit creation.
pgFormatter SQL formatpassed.squawk migration lintpassed.
Sibling overlap
These open MRs also add a migration and touch the shared generated files: !2697 (merged) (20260915170000), !2695 (merged) (20260915170840), and !2659 (merged) (20260915160000). !2630 (merged), !2662 (merged), !2681 (merged), and !2579 (merged) have merged, which moved main's applied head to 20260915160000.
Every one of those versions is below this branch's 20260915180000, so this branch needs no renumber. goose runs with WithAllowOutofOrder(false), so the merge order matters in one direction only: if this MR merges first, each of the three renumbers above 20260915180000. If one of them merges first, this branch rebases the generated files and migrations_checksum_test.go and keeps its version.
Related to #291
Database Review Evidence
Migrations
Note
Timings are from CI (db:migrate matrix, goose verbose) against an
empty database, in apply / rollback order per PG version.
Production-scale validation via Database Lab is not yet available. See
Database review evidence
for the matrix rationale and how to read the numbers.
| Migration | PG 16 | PG 17 | PG 18 |
|---|---|---|---|
20260915180000_create_container_virtual_upstream_rules.sql |
OK (698.19ms / 238.25ms) | OK (213.15ms / 155.44ms) | OK (393.95ms / 159.49ms) |