feat(maven): maven remote packages schema (S14 Step 2)
What
Adds the maven_remote_packages table — the package-level cache table for
kind=2 (remote) Maven repositories, child of Step 1's
maven_remote_repositories — as Step 2 of the S14 Maven remote vertical
slice. One goose migration, the regenerated jet types and structure.sql
dump, an integration test suite asserting the schema shape and every
constraint's accept and reject paths, and an S14 spec amendment recording
the minimum coordinate length the migration enforces.
No Go production code and no behavior change: nothing reads or writes this table until Step 8 (remote cache store Lookup) and Step 9 (cache-fill writes).
Schema
Follows the npm_remote_packages boilerplate and Step 1's
DETACH-then-DROP Down shape:
PARTITION BY HASH (namespace_id)× 64, children in thepartitionsschema.- PK
(id, namespace_id); app-generated UUIDv7, no sequence or default. - Composite FK
(maven_remote_repository_id, namespace_id)→maven_remote_repositories(id, namespace_id)withON DELETE NO ACTIONper ADR-007's artifact-table rule (the cascade stops where user data begins);namespace_idFK tonamespaces(id), alsoNO ACTION. CHECK (char_length(group_id) >= 1 AND char_length(group_id) <= 255)andCHECK (char_length(artifact_id) >= 1 AND char_length(artifact_id) <= 255)— the minimum length 1 bound the S14 data model now records.- Partial unique
(namespace_id, maven_remote_repository_id, group_id, artifact_id) WHERE soft_deleted_at IS NULL— rejects a duplicate live coordinate and admits re-caching after soft deletion. - Full, non-partial
(namespace_id, maven_remote_repository_id)index — the spec-required FK-coverage index, because the partial unique cannot back the referential-integrity check on a parent delete (a soft-deleted child still blocks).
No created_at: the spec assigns provenance timestamps to
maven_remote_versions (Step 3), not to the package row.
Tests
maven_remote_packages_schema_integration_test.go (integration tag; raw SQL
is permitted in the migrations package to assert constraints). Every
acceptance clause has a named asserting test:
| Acceptance clause | Test |
|---|---|
| Applies cleanly, 64 partitions | _TableAndPartitionsExistPostUp |
| Reverts and replays cleanly | package-level TestMigrations_UpDownUp, plus _DownLockBudget and _DownReversesEveryUpObject via the shared mavenRemoteCoreMigrationTokens extension |
| In order after Step 1, no intermediate FK error | _FKRejectsAbsentRemoteRepository; every constraint test seeds through Step 1's table |
| Partial unique rejects duplicate live coordinate | _PartialUniqueRejectsDuplicateLive (SQLSTATE 23505), _PartialUniqueScopedPerRepository |
| Admits re-caching after soft delete | _PartialUniqueAllowsRecachingAfterSoftDelete |
| Full non-partial FK-coverage index, spec columns | _FKCoverageIndexNonPartial; RI behavior _RemoteRepositoryDeleteBlockedByLiveChild / _...SoftDeletedChild |
| Partitioning, PK, column set | _PartitionsByHashOfNamespaceID, _PrimaryKey, _Columns, _PartitionRoutingByHashOfNamespaceID |
| 255 CHECKs at/over limit | _GroupAndArtifactLengthCHECKs (SQLSTATE 23514) |
| Coordinate CHECKs reject empty | _GroupAndArtifactNonEmptyCHECKs (SQLSTATE 23514) |
| Namespace FK NO ACTION | _NamespaceFKIsNoAction |
Constraint-rejection assertions pin the exact SQLSTATE (23514, 23503,
23505) rather than "an error occurred", as in Step 1.
The suite was diffed subtest-by-subtest against the npm remote packages equivalent and Step 1's repositories suite for dropped coverage; every mirrored subtest class has a counterpart, including the at-limit boundary pin and both soft-deleted-child RI pins.
MR size
~1348 lines of reviewable code (500-line migration + 848-line test), over the 500 LOC ceiling in the development model, which asks for a split or a justification here.
Justification: as in Step 1, roughly 440 of the migration's 500 lines are
the mechanical 64-partition CREATE DDL and its DETACH/DROP reverse —
64 near-identical statements, reviewable as a block once the first is read.
The generated jet types and the structure.sql dump are excluded from the
count as generated artifacts. The novel surface is the table body (~40
lines) and the test suite. Splitting the partition DDL from the table it
partitions would produce a non-applying migration, so the step is already
at its minimum reviewable size.
End-to-end scenario catalogs
No scenario added or invalidated. docs/testing/ holds no Maven catalog
yet — authoring the first one is a separate docs concern — and this step
ships no request path, so there is no observable behavior to cover. Step
18's hermetic proxy harness is the automated coverage for the S14 read
paths.
Conformance
Not applicable: this step implements no Maven protocol behavior. Conformance runs against the read paths landing in Steps 11 and 14 through 16.
Notes for reviewers
- The migration declares
-- +goose NO TRANSACTIONfor the same reason as Step 1: a single transaction would hold all 128AccessExclusiveLocks until commit; the per-statement layout releases them as it goes. The accepted cost — a partway-interruptedDownis not replayable — is documented in the migration header. - The final commit amends the merged plan: Steps 2–4's Files entries now
declare the
mavenRemoteCoreMigrationTokensappend to Step 1's shared static Down-test slice, which the implementation requires and the pre-push step validation surfaced as undeclared scope.
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 |
|---|---|---|---|
20260730120000_create_maven_remote_packages.sql |
OK (1.32s / 6.48s) | OK (514.77ms / 6.73s) | OK (521.76ms / 6.69s) |
Migration notes:
- Up/down asymmetry: rollback is 5-13x slower than apply across all PG
versions (6.48s-6.73s down vs 515ms-1.32s up). This is expected for the
64-partition
DETACH-then-DROPDown shape and is consistent with Step 1'smaven_remote_repositoriesmigration (4.36s-7.17s down) and the equivalent npm migration (4.42s-6.72s down). The Up direction - the boot-relevant phase - peaks at 1.32s, well within the 5-minute boot budget. - PG 16 apply (1.32s) is ~2.5x the PG 17/18 applies (~520ms). The sibling
maven_remote_repositoriesmigration in the same jobs shows the same ratio (1.18s vs 482-576ms), so this reads as PG 16 runner variance rather than anything migration-specific.
Related to #286 (closed)