feat(managementapi): serve dist_tags on the version resource
Why
The Version resource carries no tag information. A Tags column on the versions list, and the dist-tag badges on the version detail, need a second listNpmDistTags request per page and a client-side join on version_id: up to 10 extra requests at the 1,000-tag cap. monolith/S14 deferred its Tags column for that cost. This adds dist_tags to the resource, so one request serves the column. Issue: Versions list: add per-version npm dist-tags so... (#1153 - closed) • Hayley Swimelar. Umbrella: Management API contract gaps block the version ... (#1149) • Hayley Swimelar.
What
Versionis one struct and one OpenAPI schema across four mappers, so the field is on every version: filled on hosted npm,[]elsewhere (Maven has no tags, npm remote has nonpm_tagsrows).DistTagNames.MarshalJSONrenders nil as[]in one place, so no mapper can emitnull.- One bulk read per page:
NpmDistTagNamesByVersionIDs, scoped by namespace and package,INon the page's ids (capped at 100),LIMIT 1001per statement with a name-keyset continuation. One statement per page at the defaultnpm.max_tags_per_packageof 1,000, one more per further 1,000 tags above it, never a 500. - A failed tag read after a successful page fetch is a 500 for the whole page, because a silent
[]is indistinguishable from an untagged version. - Names ascend by tag name.
- The two statements are not one transaction: the read skew the eleven-request client-side join had is accepted, now confined to one request.
- Six lines of S17 change here rather than in a spec MR ahead of the code, because the OpenAPI document this MR edits is the contract.
Coordination
Three sibling MRs overlap this one. The container fast-follow contract rewrites two of the S17 lines this MR edits and calls the container manifest the one stored-fields exception, so after both land the sentence says two: docs(specs): declare the S17 container-redesign... (!2317 - merged) • Hayley Swimelar • 19.4. The publish-attribution fix appends rows at the same two points in docs/testing/e2e/npm.md: fix(datastore): write gitlab_user_id on hosted ... (!2328 - merged) • Hayley Swimelar • 19.4. The list-filters MR edits other regions of api/openapi/v1.yaml and merges clean: feat(managementapi): accept repeated format and... (!2329 - merged) • Hayley Swimelar • 19.4. Measured with git merge-tree against heads 1f9a37e87, c69ea7825, and ed6aab972. This MR merges after the first two and rebases onto whichever lands first.
Size
1,395 reviewable LOC, over the 500 guardrail. Splitting the store method from its only caller lands a method with no caller, and the handler review needs the store half open to check the bound and the one-call-per-page claim. If a reviewer wants the split anyway, the seam is the datastore group as part 1 of 2.
| Group | LOC |
|---|---|
Datastore production (npm_tags.go, query_names.go) |
116 |
| Management API production | 127 |
| Datastore unit tests | 181 |
| Management API unit tests, fakes, and the cmd stub | 607 |
| Integration tests, both packages | 337 |
Contract and docs (v1.yaml, S17, e2e catalog) |
27 |
Production Go is 243 of the 1,395.
Governing ADRs
ADR-014 wants list elements to carry what a list view renders, child fields fetched in one batched call per page: dist_tags is that, fetched that way. ADR-009: additive field, pagination unchanged. ADR-004: the 1,000-tag default the one-statement claim holds at. ADR-021: the versions routes already require read_artifact, which the dist-tag routes require too. No deviation.
Test plan
- Unit:
go test ./internal/datastore/ ./internal/managementapi/. - Integration,
-tags=integration:TestNpmTagStore_NpmDistTagNamesByVersionIDs,TestNpmTagStore_NpmDistTagNamesByVersionIDs_IsIndexBacked, andTestVersionFileHandlersIntegration_NpmDistTagsOnListAndDetail. Thetest:integrationandtest:integration:datastoretraces should name all three. - Contract:
TestVersionFileHandlers_ResponsesMatchOpenAPIContract,TestContract_VersionSchema_DeclaresDistTags, and thelint:openapijob. - By hand, on a hosted npm repository: publish a version,
npm dist-tag adda second tag, then GET the versions list and the version detail. Both carrydist_tagswith both names ascending, and[]on an untagged version.
Spec coverage
Spec: the issue's Ask and docs/specs/S17-rest-management-api.md (AC #22 (closed) versions list and detail, AC #27 (closed) empty parent lists [], AC #98 dist-tag routes hosted-only).
| # | Criterion | Tests |
|---|---|---|
| AC-1 | dist_tags on every version of the npm hosted list names exactly the tags bound to it |
TestVersionListHandler_NpmSerializesDistTagsPerVersion, TestVersionFileHandlersIntegration_NpmDistTagsOnListAndDetail, TestNpmTagStore_NpmDistTagNamesByVersionIDs |
| AC-2 | The same array on the version detail | TestVersionDetailHandler_NpmSerializesDistTags, TestVersionFileHandlersIntegration_NpmDistTagsOnListAndDetail |
| AC-3 | One dist-tag store call per page, never per row, and none on an empty page | TestVersionListHandler_NpmSerializesDistTagsPerVersion (one namesCalls entry), TestVersionListHandler_NpmEmptyPageIssuesNoDistTagRead, TestVersionDetailHandler_NpmSerializesDistTags (one call, one id) |
| AC-4 | Always present, always an array, [] never null, on Maven hosted, Maven remote, npm remote, and an untagged npm hosted version |
TestVersion_ZeroValueMarshalsDistTagsAsEmptyArray, TestDistTagNames_MarshalJSON, TestVersionMappers_DistTagsNeverNull, TestVersionFileRoutes_DistTagsIsEmptyArrayOffHostedNpm (Maven hosted, npm remote), TestMavenRemoteVersion_DistTagsIsEmptyArray (Maven remote), TestVersionListHandler_NpmSerializesDistTagsPerVersion (untagged npm hosted), TestVersionFileHandlersIntegration_NpmRemoteWalksAndDetails (npm remote, real DB), TestVersionFileHandlers_ResponsesMatchOpenAPIContract |
| AC-5 | Names ascend by tag name | TestNpmDistTagNamesByVersionIDsStmt (ORDER BY, the pin), TestNpmTagStore_NpmDistTagNamesByVersionIDs (non-sorted seed order), TestVersionListHandler_NpmSerializesDistTagsPerVersion, TestVersionFileHandlersIntegration_NpmDistTagsOnListAndDetail (next-then-latest seed) |
| AC-6 | The read is scoped by namespace and package, capped on its id set, and bounded per statement | TestNpmTagStore_NpmDistTagNamesByVersionIDs (cross-package and cross-namespace subtests), TestNpmTagStore_NpmDistTagNamesByVersionIDs_ArgumentGuards, TestNpmTagStore_NpmDistTagNamesByVersionIDs_AcceptsABatchLimitSet, TestNpmDistTagVersionIDsBatchLimit_AdmitsAFullVersionsPage, TestNpmDistTagNamesByVersionIDsStmt (args carry 1001), TestMaxPageSize_MatchesRemoteDetailsBatchCaps |
| AC-7 | The read is index-backed on one partition and carries the package predicate | TestNpmTagStore_NpmDistTagNamesByVersionIDs_IsIndexBacked |
| AC-8 | Non-hosted-npm paths issue no dist-tag read | TestVersionFileRoutes_DistTagsIsEmptyArrayOffHostedNpm (empty namesCalls) |
| AC-9 | The contract declares the field, required and typed, and handler output validates against it | TestContract_VersionSchema_DeclaresDistTags, TestVersionFileHandlers_ResponsesMatchOpenAPIContract ("version list 200 npm hosted with dist-tags" row), lint:openapi (CI job, not a Go test) |
| AC-10 | A package past one batch is served whole, not truncated and not failed | TestNpmTagStore_NpmDistTagNamesByVersionIDs ("a set past one batch is served whole") |
| # | Error case | Tests |
|---|---|---|
| E-1 | Dist-tag read fails after the page fetch: 500, whole page | TestVersionFileRoutes_StoreFailureReturns500 ("npm versions page dist-tags failure") |
| E-2 | Dist-tag read fails on the detail: 500 | TestVersionFileRoutes_StoreFailureReturns500 ("npm version detail dist-tags failure") |
| E-3 | Store guards reject nil ctx, zero ids, empty or oversized id set | TestNpmTagStore_NpmDistTagNamesByVersionIDs_ArgumentGuards |
| # | Security concern | Tests |
|---|---|---|
| S-1 | Tenant isolation: another namespace's tag rows never appear | TestNpmTagStore_NpmDistTagNamesByVersionIDs ("excludes another namespace's rows") |
| S-2 | No new authorization surface: versions routes already require read_artifact, the permission dist-tag reads require |
Existing binding_enforcement_test.go matrix. Not re-tested here. |
| S-3 | A tag bound under another package never leaks onto this package's versions | TestNpmTagStore_NpmDistTagNamesByVersionIDs ("excludes a tag bound under another package"), TestNpmDistTagNamesByVersionIDsStmt (package equality in the SQL), TestNpmTagStore_NpmDistTagNamesByVersionIDs_IsIndexBacked (package predicate in the plan) |
Database Review Evidence
Queries
Note
Plans are from EXPLAIN (ANALYZE, BUFFERS) against an ephemeral
PostgreSQL 17 container (matching GL_PG_CURR_VERSION from
.gitlab-ci-other-versions.yml), with synthesized seed data rolled
back per query and the container torn down at the end of the run.
Numbers reflect moderate cardinality and do not capture
production-scale effects. See
Database review evidence
for seed sizing, methodology, and the anomalies the skill flags.
Expand each row's details for the seed shape, rendered SQL, bound args,
and raw plan.
| Method | Plan node | Index | Rows (plan / actual) | Cost | Time | Buffers (hit / read) | Partitions |
|---|---|---|---|---|---|---|---|
datastore.NpmDistTagNamesByVersionIDs.AfterNameEmpty.IDs10 |
Limit | npm_tags_p11_namespace_id_npm_version_id_idx |
222 / 1000 | 158.83 | 0.334ms | 18 / 0 | 1/64 |
datastore.NpmDistTagNamesByVersionIDs.AfterNameEmpty.IDs100 |
Limit | npm_tags_p11_namespace_id_npm_version_id_idx |
444 / 1001 | 190.52 | 0.607ms | 34 / 0 | 1/64 |
datastore.NpmDistTagNamesByVersionIDs.AfterNameNonEmpty.IDs100 |
Limit | npm_tags_p11_namespace_id_npm_version_id_idx |
296 / 1000 | 186.07 | 0.490ms | 34 / 0 | 1/64 |
datastore.NpmDistTagNamesByVersionIDs.AfterNameEmpty.IDs10
Summary: Plan matches the read's intent: the namespace literal prunes npm_tags to one of 64 partitions (npm_tags_p11), the ten version ids drive a Bitmap Index Scan on the partition of index_npm_tags_on_ns_id_version_id, and npm_package_id is a heap Filter, so a tag under another package never surfaces. The Sort under the Limit holds the 1,000 matched rows (quicksort, 95 kB), and the 222-row estimate undercounts because the planner treats the package and version-id predicates as independent, within the 10x threshold. Same shape as the measurement at 6e037cad1 (18 shared buffers against 34, 0.33 ms against 0.40 ms), here at default planner settings rather than on an enable_seqscan=off connection. No anomalies.
Seed shape: namespaces=2, repositories=2, npm_repositories=2, npm_packages=3, npm_versions=140, npm_tags=6000 (all 6,000 tags in partitions.npm_tags_p11: 100 per version on 20 of the target package's 100 versions, 2,000 on a sibling package in the same namespace, and 2,000 on a package in a second namespace hashed to the same partition)
Rendered SQL:
SELECT npm_tags.npm_version_id AS "npm_tags.npm_version_id",
npm_tags.name AS "npm_tags.name"
FROM public.npm_tags
WHERE ((npm_tags.namespace_id = $1::uuid) AND (npm_tags.npm_package_id = $2::uuid)) AND (npm_tags.npm_version_id IN ($3::uuid, $4::uuid, $5::uuid, $6::uuid, $7::uuid, $8::uuid, $9::uuid, $10::uuid, $11::uuid, $12::uuid))
ORDER BY npm_tags.name ASC
LIMIT $13;Bound args: [019a0000-0000-7000-8000-00000000000a, 019a0000-0000-7000-8000-0000000000b1, 019a0000-0000-7000-8000-000100000001, 019a0000-0000-7000-8000-000100000002, 019a0000-0000-7000-8000-000100000003, 019a0000-0000-7000-8000-000100000004, 019a0000-0000-7000-8000-000100000005, 019a0000-0000-7000-8000-000100000006, 019a0000-0000-7000-8000-000100000007, 019a0000-0000-7000-8000-000100000008, 019a0000-0000-7000-8000-000100000009, 019a0000-0000-7000-8000-00010000000a, 1001]
Plan (EXPLAIN (ANALYZE, BUFFERS) output):
Limit (cost=158.27..158.83 rows=222 width=48) (actual time=0.266..0.334 rows=1000 loops=1)
Buffers: shared hit=18
-> Sort (cost=158.27..158.83 rows=222 width=48) (actual time=0.265..0.294 rows=1000 loops=1)
Sort Key: npm_tags.name
Sort Method: quicksort Memory: 95kB
Buffers: shared hit=18
-> Bitmap Heap Scan on npm_tags_p11 npm_tags (cost=41.28..149.62 rows=222 width=48) (actual time=0.030..0.108 rows=1000 loops=1)
Recheck Cond: ((namespace_id = '019a0000-0000-7000-8000-00000000000a'::uuid) AND (npm_version_id = ANY ('{019a0000-0000-7000-8000-000100000001,019a0000-0000-7000-8000-000100000002,019a0000-0000-7000-8000-000100000003,019a0000-0000-7000-8000-000100000004,019a0000-0000-7000-8000-000100000005,019a0000-0000-7000-8000-000100000006,019a0000-0000-7000-8000-000100000007,019a0000-0000-7000-8000-000100000008,019a0000-0000-7000-8000-000100000009,019a0000-0000-7000-8000-00010000000a}'::uuid[])))
Filter: (npm_package_id = '019a0000-0000-7000-8000-0000000000b1'::uuid)
Heap Blocks: exact=16
Buffers: shared hit=18
-> Bitmap Index Scan on npm_tags_p11_namespace_id_npm_version_id_idx (cost=0.00..41.20 rows=667 width=0) (actual time=0.025..0.025 rows=1000 loops=1)
Index Cond: ((namespace_id = '019a0000-0000-7000-8000-00000000000a'::uuid) AND (npm_version_id = ANY ('{019a0000-0000-7000-8000-000100000001,019a0000-0000-7000-8000-000100000002,019a0000-0000-7000-8000-000100000003,019a0000-0000-7000-8000-000100000004,019a0000-0000-7000-8000-000100000005,019a0000-0000-7000-8000-000100000006,019a0000-0000-7000-8000-000100000007,019a0000-0000-7000-8000-000100000008,019a0000-0000-7000-8000-000100000009,019a0000-0000-7000-8000-00010000000a}'::uuid[])))
Buffers: shared hit=2
Planning:
Buffers: shared hit=35
Planning Time: 0.224 ms
Execution Time: 0.385 msTimings: planning 0.224ms, execution 0.385ms, total 0.609ms.
datastore.NpmDistTagNamesByVersionIDs.AfterNameEmpty.IDs100
Summary: The full 100-id page (20 tagged versions, 80 untagged) plans the same way: one partition, one probe of the version-id index per id, npm_package_id as a Filter, and a Sort of the 2,000 matched rows (quicksort, 189 kB) under the 1,001-row Limit, which returns the probe row that starts the continuation batch. Per-statement work is the id set's tag count, which npm.max_tags_per_package caps (default 1,000, no validated ceiling). The (namespace_id, npm_package_id, npm_version_id, name) index the Non-goals name is the follow-up for a cap past about 10,000. No anomalies.
Seed shape: namespaces=2, repositories=2, npm_repositories=2, npm_packages=3, npm_versions=140, npm_tags=6000 (all 6,000 tags in partitions.npm_tags_p11: 100 per version on 20 of the target package's 100 versions, 2,000 on a sibling package in the same namespace, and 2,000 on a package in a second namespace hashed to the same partition)
Rendered SQL:
SELECT npm_tags.npm_version_id AS "npm_tags.npm_version_id",
npm_tags.name AS "npm_tags.name"
FROM public.npm_tags
WHERE ((npm_tags.namespace_id = $1::uuid) AND (npm_tags.npm_package_id = $2::uuid)) AND (npm_tags.npm_version_id IN ($3::uuid, $4::uuid, $5::uuid, $6::uuid, $7::uuid, $8::uuid, $9::uuid, $10::uuid, $11::uuid, $12::uuid, $13::uuid, $14::uuid, $15::uuid, $16::uuid, $17::uuid, $18::uuid, $19::uuid, $20::uuid, $21::uuid, $22::uuid, $23::uuid, $24::uuid, $25::uuid, $26::uuid, $27::uuid, $28::uuid, $29::uuid, $30::uuid, $31::uuid, $32::uuid, $33::uuid, $34::uuid, $35::uuid, $36::uuid, $37::uuid, $38::uuid, $39::uuid, $40::uuid, $41::uuid, $42::uuid, $43::uuid, $44::uuid, $45::uuid, $46::uuid, $47::uuid, $48::uuid, $49::uuid, $50::uuid, $51::uuid, $52::uuid, $53::uuid, $54::uuid, $55::uuid, $56::uuid, $57::uuid, $58::uuid, $59::uuid, $60::uuid, $61::uuid, $62::uuid, $63::uuid, $64::uuid, $65::uuid, $66::uuid, $67::uuid, $68::uuid, $69::uuid, $70::uuid, $71::uuid, $72::uuid, $73::uuid, $74::uuid, $75::uuid, $76::uuid, $77::uuid, $78::uuid, $79::uuid, $80::uuid, $81::uuid, $82::uuid, $83::uuid, $84::uuid, $85::uuid, $86::uuid, $87::uuid, $88::uuid, $89::uuid, $90::uuid, $91::uuid, $92::uuid, $93::uuid, $94::uuid, $95::uuid, $96::uuid, $97::uuid, $98::uuid, $99::uuid, $100::uuid, $101::uuid, $102::uuid))
ORDER BY npm_tags.name ASC
LIMIT $103;Bound args: [019a0000-0000-7000-8000-00000000000a, 019a0000-0000-7000-8000-0000000000b1, 019a0000-0000-7000-8000-000100000001, 019a0000-0000-7000-8000-000100000002, 019a0000-0000-7000-8000-000100000003, 019a0000-0000-7000-8000-000100000004, 019a0000-0000-7000-8000-000100000005, 019a0000-0000-7000-8000-000100000006, 019a0000-0000-7000-8000-000100000007, 019a0000-0000-7000-8000-000100000008, 019a0000-0000-7000-8000-000100000009, 019a0000-0000-7000-8000-00010000000a, 019a0000-0000-7000-8000-00010000000b, 019a0000-0000-7000-8000-00010000000c, 019a0000-0000-7000-8000-00010000000d, 019a0000-0000-7000-8000-00010000000e, 019a0000-0000-7000-8000-00010000000f, 019a0000-0000-7000-8000-000100000010, 019a0000-0000-7000-8000-000100000011, 019a0000-0000-7000-8000-000100000012, 019a0000-0000-7000-8000-000100000013, 019a0000-0000-7000-8000-000100000014, 019a0000-0000-7000-8000-000100000015, 019a0000-0000-7000-8000-000100000016, 019a0000-0000-7000-8000-000100000017, 019a0000-0000-7000-8000-000100000018, 019a0000-0000-7000-8000-000100000019, 019a0000-0000-7000-8000-00010000001a, 019a0000-0000-7000-8000-00010000001b, 019a0000-0000-7000-8000-00010000001c, 019a0000-0000-7000-8000-00010000001d, 019a0000-0000-7000-8000-00010000001e, 019a0000-0000-7000-8000-00010000001f, 019a0000-0000-7000-8000-000100000020, 019a0000-0000-7000-8000-000100000021, 019a0000-0000-7000-8000-000100000022, 019a0000-0000-7000-8000-000100000023, 019a0000-0000-7000-8000-000100000024, 019a0000-0000-7000-8000-000100000025, 019a0000-0000-7000-8000-000100000026, 019a0000-0000-7000-8000-000100000027, 019a0000-0000-7000-8000-000100000028, 019a0000-0000-7000-8000-000100000029, 019a0000-0000-7000-8000-00010000002a, 019a0000-0000-7000-8000-00010000002b, 019a0000-0000-7000-8000-00010000002c, 019a0000-0000-7000-8000-00010000002d, 019a0000-0000-7000-8000-00010000002e, 019a0000-0000-7000-8000-00010000002f, 019a0000-0000-7000-8000-000100000030, 019a0000-0000-7000-8000-000100000031, 019a0000-0000-7000-8000-000100000032, 019a0000-0000-7000-8000-000100000033, 019a0000-0000-7000-8000-000100000034, 019a0000-0000-7000-8000-000100000035, 019a0000-0000-7000-8000-000100000036, 019a0000-0000-7000-8000-000100000037, 019a0000-0000-7000-8000-000100000038, 019a0000-0000-7000-8000-000100000039, 019a0000-0000-7000-8000-00010000003a, 019a0000-0000-7000-8000-00010000003b, 019a0000-0000-7000-8000-00010000003c, 019a0000-0000-7000-8000-00010000003d, 019a0000-0000-7000-8000-00010000003e, 019a0000-0000-7000-8000-00010000003f, 019a0000-0000-7000-8000-000100000040, 019a0000-0000-7000-8000-000100000041, 019a0000-0000-7000-8000-000100000042, 019a0000-0000-7000-8000-000100000043, 019a0000-0000-7000-8000-000100000044, 019a0000-0000-7000-8000-000100000045, 019a0000-0000-7000-8000-000100000046, 019a0000-0000-7000-8000-000100000047, 019a0000-0000-7000-8000-000100000048, 019a0000-0000-7000-8000-000100000049, 019a0000-0000-7000-8000-00010000004a, 019a0000-0000-7000-8000-00010000004b, 019a0000-0000-7000-8000-00010000004c, 019a0000-0000-7000-8000-00010000004d, 019a0000-0000-7000-8000-00010000004e, 019a0000-0000-7000-8000-00010000004f, 019a0000-0000-7000-8000-000100000050, 019a0000-0000-7000-8000-000100000051, 019a0000-0000-7000-8000-000100000052, 019a0000-0000-7000-8000-000100000053, 019a0000-0000-7000-8000-000100000054, 019a0000-0000-7000-8000-000100000055, 019a0000-0000-7000-8000-000100000056, 019a0000-0000-7000-8000-000100000057, 019a0000-0000-7000-8000-000100000058, 019a0000-0000-7000-8000-000100000059, 019a0000-0000-7000-8000-00010000005a, 019a0000-0000-7000-8000-00010000005b, 019a0000-0000-7000-8000-00010000005c, 019a0000-0000-7000-8000-00010000005d, 019a0000-0000-7000-8000-00010000005e, 019a0000-0000-7000-8000-00010000005f, 019a0000-0000-7000-8000-000100000060, 019a0000-0000-7000-8000-000100000061, 019a0000-0000-7000-8000-000100000062, 019a0000-0000-7000-8000-000100000063, 019a0000-0000-7000-8000-000100000064, 1001]
Plan (EXPLAIN (ANALYZE, BUFFERS) output):
Limit (cost=189.41..190.52 rows=444 width=48) (actual time=0.539..0.607 rows=1001 loops=1)
Buffers: shared hit=34
-> Sort (cost=189.41..190.52 rows=444 width=48) (actual time=0.539..0.567 rows=1001 loops=1)
Sort Key: npm_tags.name
Sort Method: quicksort Memory: 189kB
Buffers: shared hit=34
-> Bitmap Heap Scan on npm_tags_p11 npm_tags (cost=48.22..169.88 rows=444 width=48) (actual time=0.045..0.203 rows=2000 loops=1)
Recheck Cond: ((namespace_id = '019a0000-0000-7000-8000-00000000000a'::uuid) AND (npm_version_id = ANY ('{019a0000-0000-7000-8000-000100000001,019a0000-0000-7000-8000-000100000002,019a0000-0000-7000-8000-000100000003,019a0000-0000-7000-8000-000100000004,019a0000-0000-7000-8000-000100000005,019a0000-0000-7000-8000-000100000006,019a0000-0000-7000-8000-000100000007,019a0000-0000-7000-8000-000100000008,019a0000-0000-7000-8000-000100000009,019a0000-0000-7000-8000-00010000000a,019a0000-0000-7000-8000-00010000000b,019a0000-0000-7000-8000-00010000000c,019a0000-0000-7000-8000-00010000000d,019a0000-0000-7000-8000-00010000000e,019a0000-0000-7000-8000-00010000000f,019a0000-0000-7000-8000-000100000010,019a0000-0000-7000-8000-000100000011,019a0000-0000-7000-8000-000100000012,019a0000-0000-7000-8000-000100000013,019a0000-0000-7000-8000-000100000014,019a0000-0000-7000-8000-000100000015,019a0000-0000-7000-8000-000100000016,019a0000-0000-7000-8000-000100000017,019a0000-0000-7000-8000-000100000018,019a0000-0000-7000-8000-000100000019,019a0000-0000-7000-8000-00010000001a,019a0000-0000-7000-8000-00010000001b,019a0000-0000-7000-8000-00010000001c,019a0000-0000-7000-8000-00010000001d,019a0000-0000-7000-8000-00010000001e,019a0000-0000-7000-8000-00010000001f,019a0000-0000-7000-8000-000100000020,019a0000-0000-7000-8000-000100000021,019a0000-0000-7000-8000-000100000022,019a0000-0000-7000-8000-000100000023,019a0000-0000-7000-8000-000100000024,019a0000-0000-7000-8000-000100000025,019a0000-0000-7000-8000-000100000026,019a0000-0000-7000-8000-000100000027,019a0000-0000-7000-8000-000100000028,019a0000-0000-7000-8000-000100000029,019a0000-0000-7000-8000-00010000002a,019a0000-0000-7000-8000-00010000002b,019a0000-0000-7000-8000-00010000002c,019a0000-0000-7000-8000-00010000002d,019a0000-0000-7000-8000-00010000002e,019a0000-0000-7000-8000-00010000002f,019a0000-0000-7000-8000-000100000030,019a0000-0000-7000-8000-000100000031,019a0000-0000-7000-8000-000100000032,019a0000-0000-7000-8000-000100000033,019a0000-0000-7000-8000-000100000034,019a0000-0000-7000-8000-000100000035,019a0000-0000-7000-8000-000100000036,019a0000-0000-7000-8000-000100000037,019a0000-0000-7000-8000-000100000038,019a0000-0000-7000-8000-000100000039,019a0000-0000-7000-8000-00010000003a,019a0000-0000-7000-8000-00010000003b,019a0000-0000-7000-8000-00010000003c,019a0000-0000-7000-8000-00010000003d,019a0000-0000-7000-8000-00010000003e,019a0000-0000-7000-8000-00010000003f,019a0000-0000-7000-8000-000100000040,019a0000-0000-7000-8000-000100000041,019a0000-0000-7000-8000-000100000042,019a0000-0000-7000-8000-000100000043,019a0000-0000-7000-8000-000100000044,019a0000-0000-7000-8000-000100000045,019a0000-0000-7000-8000-000100000046,019a0000-0000-7000-8000-000100000047,019a0000-0000-7000-8000-000100000048,019a0000-0000-7000-8000-000100000049,019a0000-0000-7000-8000-00010000004a,019a0000-0000-7000-8000-00010000004b,019a0000-0000-7000-8000-00010000004c,019a0000-0000-7000-8000-00010000004d,019a0000-0000-7000-8000-00010000004e,019a0000-0000-7000-8000-00010000004f,019a0000-0000-7000-8000-000100000050,019a0000-0000-7000-8000-000100000051,019a0000-0000-7000-8000-000100000052,019a0000-0000-7000-8000-000100000053,019a0000-0000-7000-8000-000100000054,019a0000-0000-7000-8000-000100000055,019a0000-0000-7000-8000-000100000056,019a0000-0000-7000-8000-000100000057,019a0000-0000-7000-8000-000100000058,019a0000-0000-7000-8000-000100000059,019a0000-0000-7000-8000-00010000005a,019a0000-0000-7000-8000-00010000005b,019a0000-0000-7000-8000-00010000005c,019a0000-0000-7000-8000-00010000005d,019a0000-0000-7000-8000-00010000005e,019a0000-0000-7000-8000-00010000005f,019a0000-0000-7000-8000-000100000060,019a0000-0000-7000-8000-000100000061,019a0000-0000-7000-8000-000100000062,019a0000-0000-7000-8000-000100000063,019a0000-0000-7000-8000-000100000064}'::uuid[])))
Filter: (npm_package_id = '019a0000-0000-7000-8000-0000000000b1'::uuid)
Heap Blocks: exact=31
Buffers: shared hit=34
-> Bitmap Index Scan on npm_tags_p11_namespace_id_npm_version_id_idx (cost=0.00..47.86 rows=1333 width=0) (actual time=0.038..0.038 rows=2000 loops=1)
Index Cond: ((namespace_id = '019a0000-0000-7000-8000-00000000000a'::uuid) AND (npm_version_id = ANY ('{019a0000-0000-7000-8000-000100000001,019a0000-0000-7000-8000-000100000002,019a0000-0000-7000-8000-000100000003,019a0000-0000-7000-8000-000100000004,019a0000-0000-7000-8000-000100000005,019a0000-0000-7000-8000-000100000006,019a0000-0000-7000-8000-000100000007,019a0000-0000-7000-8000-000100000008,019a0000-0000-7000-8000-000100000009,019a0000-0000-7000-8000-00010000000a,019a0000-0000-7000-8000-00010000000b,019a0000-0000-7000-8000-00010000000c,019a0000-0000-7000-8000-00010000000d,019a0000-0000-7000-8000-00010000000e,019a0000-0000-7000-8000-00010000000f,019a0000-0000-7000-8000-000100000010,019a0000-0000-7000-8000-000100000011,019a0000-0000-7000-8000-000100000012,019a0000-0000-7000-8000-000100000013,019a0000-0000-7000-8000-000100000014,019a0000-0000-7000-8000-000100000015,019a0000-0000-7000-8000-000100000016,019a0000-0000-7000-8000-000100000017,019a0000-0000-7000-8000-000100000018,019a0000-0000-7000-8000-000100000019,019a0000-0000-7000-8000-00010000001a,019a0000-0000-7000-8000-00010000001b,019a0000-0000-7000-8000-00010000001c,019a0000-0000-7000-8000-00010000001d,019a0000-0000-7000-8000-00010000001e,019a0000-0000-7000-8000-00010000001f,019a0000-0000-7000-8000-000100000020,019a0000-0000-7000-8000-000100000021,019a0000-0000-7000-8000-000100000022,019a0000-0000-7000-8000-000100000023,019a0000-0000-7000-8000-000100000024,019a0000-0000-7000-8000-000100000025,019a0000-0000-7000-8000-000100000026,019a0000-0000-7000-8000-000100000027,019a0000-0000-7000-8000-000100000028,019a0000-0000-7000-8000-000100000029,019a0000-0000-7000-8000-00010000002a,019a0000-0000-7000-8000-00010000002b,019a0000-0000-7000-8000-00010000002c,019a0000-0000-7000-8000-00010000002d,019a0000-0000-7000-8000-00010000002e,019a0000-0000-7000-8000-00010000002f,019a0000-0000-7000-8000-000100000030,019a0000-0000-7000-8000-000100000031,019a0000-0000-7000-8000-000100000032,019a0000-0000-7000-8000-000100000033,019a0000-0000-7000-8000-000100000034,019a0000-0000-7000-8000-000100000035,019a0000-0000-7000-8000-000100000036,019a0000-0000-7000-8000-000100000037,019a0000-0000-7000-8000-000100000038,019a0000-0000-7000-8000-000100000039,019a0000-0000-7000-8000-00010000003a,019a0000-0000-7000-8000-00010000003b,019a0000-0000-7000-8000-00010000003c,019a0000-0000-7000-8000-00010000003d,019a0000-0000-7000-8000-00010000003e,019a0000-0000-7000-8000-00010000003f,019a0000-0000-7000-8000-000100000040,019a0000-0000-7000-8000-000100000041,019a0000-0000-7000-8000-000100000042,019a0000-0000-7000-8000-000100000043,019a0000-0000-7000-8000-000100000044,019a0000-0000-7000-8000-000100000045,019a0000-0000-7000-8000-000100000046,019a0000-0000-7000-8000-000100000047,019a0000-0000-7000-8000-000100000048,019a0000-0000-7000-8000-000100000049,019a0000-0000-7000-8000-00010000004a,019a0000-0000-7000-8000-00010000004b,019a0000-0000-7000-8000-00010000004c,019a0000-0000-7000-8000-00010000004d,019a0000-0000-7000-8000-00010000004e,019a0000-0000-7000-8000-00010000004f,019a0000-0000-7000-8000-000100000050,019a0000-0000-7000-8000-000100000051,019a0000-0000-7000-8000-000100000052,019a0000-0000-7000-8000-000100000053,019a0000-0000-7000-8000-000100000054,019a0000-0000-7000-8000-000100000055,019a0000-0000-7000-8000-000100000056,019a0000-0000-7000-8000-000100000057,019a0000-0000-7000-8000-000100000058,019a0000-0000-7000-8000-000100000059,019a0000-0000-7000-8000-00010000005a,019a0000-0000-7000-8000-00010000005b,019a0000-0000-7000-8000-00010000005c,019a0000-0000-7000-8000-00010000005d,019a0000-0000-7000-8000-00010000005e,019a0000-0000-7000-8000-00010000005f,019a0000-0000-7000-8000-000100000060,019a0000-0000-7000-8000-000100000061,019a0000-0000-7000-8000-000100000062,019a0000-0000-7000-8000-000100000063,019a0000-0000-7000-8000-000100000064}'::uuid[])))
Buffers: shared hit=3
Planning:
Buffers: shared hit=35
Planning Time: 0.389 ms
Execution Time: 0.671 msTimings: planning 0.389ms, execution 0.671ms, total 1.060ms.
datastore.NpmDistTagNamesByVersionIDs.AfterNameNonEmpty.IDs100
Summary: The continuation keeps the version-id index and applies the name > boundary and npm_package_id as heap Filters, so it re-reads the id set's 2,000 rows and discards the 1,000 the first batch served (Rows Removed by Filter: 1000). The planner costs the alternative, the (namespace_id, npm_package_id, name) unique index with the boundary in its Index Cond, at 219.6 against 186.1 (28 buffers, 0.38 ms in a diagnostic run with the version-id index dropped inside the rolled-back transaction), a near-tie that TestNpmTagStore_NpmDistTagNamesByVersionIDs_IsIndexBacked deliberately leaves open. A continuation runs only above the default npm.max_tags_per_package of 1,000, and either path stays within the package's tag count. No anomalies.
Seed shape: namespaces=2, repositories=2, npm_repositories=2, npm_packages=3, npm_versions=140, npm_tags=6000 (all 6,000 tags in partitions.npm_tags_p11: 100 per version on 20 of the target package's 100 versions, 2,000 on a sibling package in the same namespace, and 2,000 on a package in a second namespace hashed to the same partition)
Rendered SQL:
SELECT npm_tags.npm_version_id AS "npm_tags.npm_version_id",
npm_tags.name AS "npm_tags.name"
FROM public.npm_tags
WHERE (((npm_tags.namespace_id = $1::uuid) AND (npm_tags.npm_package_id = $2::uuid)) AND (npm_tags.npm_version_id IN ($3::uuid, $4::uuid, $5::uuid, $6::uuid, $7::uuid, $8::uuid, $9::uuid, $10::uuid, $11::uuid, $12::uuid, $13::uuid, $14::uuid, $15::uuid, $16::uuid, $17::uuid, $18::uuid, $19::uuid, $20::uuid, $21::uuid, $22::uuid, $23::uuid, $24::uuid, $25::uuid, $26::uuid, $27::uuid, $28::uuid, $29::uuid, $30::uuid, $31::uuid, $32::uuid, $33::uuid, $34::uuid, $35::uuid, $36::uuid, $37::uuid, $38::uuid, $39::uuid, $40::uuid, $41::uuid, $42::uuid, $43::uuid, $44::uuid, $45::uuid, $46::uuid, $47::uuid, $48::uuid, $49::uuid, $50::uuid, $51::uuid, $52::uuid, $53::uuid, $54::uuid, $55::uuid, $56::uuid, $57::uuid, $58::uuid, $59::uuid, $60::uuid, $61::uuid, $62::uuid, $63::uuid, $64::uuid, $65::uuid, $66::uuid, $67::uuid, $68::uuid, $69::uuid, $70::uuid, $71::uuid, $72::uuid, $73::uuid, $74::uuid, $75::uuid, $76::uuid, $77::uuid, $78::uuid, $79::uuid, $80::uuid, $81::uuid, $82::uuid, $83::uuid, $84::uuid, $85::uuid, $86::uuid, $87::uuid, $88::uuid, $89::uuid, $90::uuid, $91::uuid, $92::uuid, $93::uuid, $94::uuid, $95::uuid, $96::uuid, $97::uuid, $98::uuid, $99::uuid, $100::uuid, $101::uuid, $102::uuid))) AND (npm_tags.name > $103::text)
ORDER BY npm_tags.name ASC
LIMIT $104;Bound args: [019a0000-0000-7000-8000-00000000000a, 019a0000-0000-7000-8000-0000000000b1, 019a0000-0000-7000-8000-000100000001, 019a0000-0000-7000-8000-000100000002, 019a0000-0000-7000-8000-000100000003, 019a0000-0000-7000-8000-000100000004, 019a0000-0000-7000-8000-000100000005, 019a0000-0000-7000-8000-000100000006, 019a0000-0000-7000-8000-000100000007, 019a0000-0000-7000-8000-000100000008, 019a0000-0000-7000-8000-000100000009, 019a0000-0000-7000-8000-00010000000a, 019a0000-0000-7000-8000-00010000000b, 019a0000-0000-7000-8000-00010000000c, 019a0000-0000-7000-8000-00010000000d, 019a0000-0000-7000-8000-00010000000e, 019a0000-0000-7000-8000-00010000000f, 019a0000-0000-7000-8000-000100000010, 019a0000-0000-7000-8000-000100000011, 019a0000-0000-7000-8000-000100000012, 019a0000-0000-7000-8000-000100000013, 019a0000-0000-7000-8000-000100000014, 019a0000-0000-7000-8000-000100000015, 019a0000-0000-7000-8000-000100000016, 019a0000-0000-7000-8000-000100000017, 019a0000-0000-7000-8000-000100000018, 019a0000-0000-7000-8000-000100000019, 019a0000-0000-7000-8000-00010000001a, 019a0000-0000-7000-8000-00010000001b, 019a0000-0000-7000-8000-00010000001c, 019a0000-0000-7000-8000-00010000001d, 019a0000-0000-7000-8000-00010000001e, 019a0000-0000-7000-8000-00010000001f, 019a0000-0000-7000-8000-000100000020, 019a0000-0000-7000-8000-000100000021, 019a0000-0000-7000-8000-000100000022, 019a0000-0000-7000-8000-000100000023, 019a0000-0000-7000-8000-000100000024, 019a0000-0000-7000-8000-000100000025, 019a0000-0000-7000-8000-000100000026, 019a0000-0000-7000-8000-000100000027, 019a0000-0000-7000-8000-000100000028, 019a0000-0000-7000-8000-000100000029, 019a0000-0000-7000-8000-00010000002a, 019a0000-0000-7000-8000-00010000002b, 019a0000-0000-7000-8000-00010000002c, 019a0000-0000-7000-8000-00010000002d, 019a0000-0000-7000-8000-00010000002e, 019a0000-0000-7000-8000-00010000002f, 019a0000-0000-7000-8000-000100000030, 019a0000-0000-7000-8000-000100000031, 019a0000-0000-7000-8000-000100000032, 019a0000-0000-7000-8000-000100000033, 019a0000-0000-7000-8000-000100000034, 019a0000-0000-7000-8000-000100000035, 019a0000-0000-7000-8000-000100000036, 019a0000-0000-7000-8000-000100000037, 019a0000-0000-7000-8000-000100000038, 019a0000-0000-7000-8000-000100000039, 019a0000-0000-7000-8000-00010000003a, 019a0000-0000-7000-8000-00010000003b, 019a0000-0000-7000-8000-00010000003c, 019a0000-0000-7000-8000-00010000003d, 019a0000-0000-7000-8000-00010000003e, 019a0000-0000-7000-8000-00010000003f, 019a0000-0000-7000-8000-000100000040, 019a0000-0000-7000-8000-000100000041, 019a0000-0000-7000-8000-000100000042, 019a0000-0000-7000-8000-000100000043, 019a0000-0000-7000-8000-000100000044, 019a0000-0000-7000-8000-000100000045, 019a0000-0000-7000-8000-000100000046, 019a0000-0000-7000-8000-000100000047, 019a0000-0000-7000-8000-000100000048, 019a0000-0000-7000-8000-000100000049, 019a0000-0000-7000-8000-00010000004a, 019a0000-0000-7000-8000-00010000004b, 019a0000-0000-7000-8000-00010000004c, 019a0000-0000-7000-8000-00010000004d, 019a0000-0000-7000-8000-00010000004e, 019a0000-0000-7000-8000-00010000004f, 019a0000-0000-7000-8000-000100000050, 019a0000-0000-7000-8000-000100000051, 019a0000-0000-7000-8000-000100000052, 019a0000-0000-7000-8000-000100000053, 019a0000-0000-7000-8000-000100000054, 019a0000-0000-7000-8000-000100000055, 019a0000-0000-7000-8000-000100000056, 019a0000-0000-7000-8000-000100000057, 019a0000-0000-7000-8000-000100000058, 019a0000-0000-7000-8000-000100000059, 019a0000-0000-7000-8000-00010000005a, 019a0000-0000-7000-8000-00010000005b, 019a0000-0000-7000-8000-00010000005c, 019a0000-0000-7000-8000-00010000005d, 019a0000-0000-7000-8000-00010000005e, 019a0000-0000-7000-8000-00010000005f, 019a0000-0000-7000-8000-000100000060, 019a0000-0000-7000-8000-000100000061, 019a0000-0000-7000-8000-000100000062, 019a0000-0000-7000-8000-000100000063, 019a0000-0000-7000-8000-000100000064, review-prep-npm_tags-name-0999, 1001]
Plan (EXPLAIN (ANALYZE, BUFFERS) output):
Limit (cost=185.33..186.07 rows=296 width=48) (actual time=0.424..0.490 rows=1000 loops=1)
Buffers: shared hit=34
-> Sort (cost=185.33..186.07 rows=296 width=48) (actual time=0.424..0.449 rows=1000 loops=1)
Sort Key: npm_tags.name
Sort Method: quicksort Memory: 95kB
Buffers: shared hit=34
-> Bitmap Heap Scan on npm_tags_p11 npm_tags (cost=48.19..173.18 rows=296 width=48) (actual time=0.131..0.256 rows=1000 loops=1)
Recheck Cond: ((namespace_id = '019a0000-0000-7000-8000-00000000000a'::uuid) AND (npm_version_id = ANY ('{019a0000-0000-7000-8000-000100000001,019a0000-0000-7000-8000-000100000002,019a0000-0000-7000-8000-000100000003,019a0000-0000-7000-8000-000100000004,019a0000-0000-7000-8000-000100000005,019a0000-0000-7000-8000-000100000006,019a0000-0000-7000-8000-000100000007,019a0000-0000-7000-8000-000100000008,019a0000-0000-7000-8000-000100000009,019a0000-0000-7000-8000-00010000000a,019a0000-0000-7000-8000-00010000000b,019a0000-0000-7000-8000-00010000000c,019a0000-0000-7000-8000-00010000000d,019a0000-0000-7000-8000-00010000000e,019a0000-0000-7000-8000-00010000000f,019a0000-0000-7000-8000-000100000010,019a0000-0000-7000-8000-000100000011,019a0000-0000-7000-8000-000100000012,019a0000-0000-7000-8000-000100000013,019a0000-0000-7000-8000-000100000014,019a0000-0000-7000-8000-000100000015,019a0000-0000-7000-8000-000100000016,019a0000-0000-7000-8000-000100000017,019a0000-0000-7000-8000-000100000018,019a0000-0000-7000-8000-000100000019,019a0000-0000-7000-8000-00010000001a,019a0000-0000-7000-8000-00010000001b,019a0000-0000-7000-8000-00010000001c,019a0000-0000-7000-8000-00010000001d,019a0000-0000-7000-8000-00010000001e,019a0000-0000-7000-8000-00010000001f,019a0000-0000-7000-8000-000100000020,019a0000-0000-7000-8000-000100000021,019a0000-0000-7000-8000-000100000022,019a0000-0000-7000-8000-000100000023,019a0000-0000-7000-8000-000100000024,019a0000-0000-7000-8000-000100000025,019a0000-0000-7000-8000-000100000026,019a0000-0000-7000-8000-000100000027,019a0000-0000-7000-8000-000100000028,019a0000-0000-7000-8000-000100000029,019a0000-0000-7000-8000-00010000002a,019a0000-0000-7000-8000-00010000002b,019a0000-0000-7000-8000-00010000002c,019a0000-0000-7000-8000-00010000002d,019a0000-0000-7000-8000-00010000002e,019a0000-0000-7000-8000-00010000002f,019a0000-0000-7000-8000-000100000030,019a0000-0000-7000-8000-000100000031,019a0000-0000-7000-8000-000100000032,019a0000-0000-7000-8000-000100000033,019a0000-0000-7000-8000-000100000034,019a0000-0000-7000-8000-000100000035,019a0000-0000-7000-8000-000100000036,019a0000-0000-7000-8000-000100000037,019a0000-0000-7000-8000-000100000038,019a0000-0000-7000-8000-000100000039,019a0000-0000-7000-8000-00010000003a,019a0000-0000-7000-8000-00010000003b,019a0000-0000-7000-8000-00010000003c,019a0000-0000-7000-8000-00010000003d,019a0000-0000-7000-8000-00010000003e,019a0000-0000-7000-8000-00010000003f,019a0000-0000-7000-8000-000100000040,019a0000-0000-7000-8000-000100000041,019a0000-0000-7000-8000-000100000042,019a0000-0000-7000-8000-000100000043,019a0000-0000-7000-8000-000100000044,019a0000-0000-7000-8000-000100000045,019a0000-0000-7000-8000-000100000046,019a0000-0000-7000-8000-000100000047,019a0000-0000-7000-8000-000100000048,019a0000-0000-7000-8000-000100000049,019a0000-0000-7000-8000-00010000004a,019a0000-0000-7000-8000-00010000004b,019a0000-0000-7000-8000-00010000004c,019a0000-0000-7000-8000-00010000004d,019a0000-0000-7000-8000-00010000004e,019a0000-0000-7000-8000-00010000004f,019a0000-0000-7000-8000-000100000050,019a0000-0000-7000-8000-000100000051,019a0000-0000-7000-8000-000100000052,019a0000-0000-7000-8000-000100000053,019a0000-0000-7000-8000-000100000054,019a0000-0000-7000-8000-000100000055,019a0000-0000-7000-8000-000100000056,019a0000-0000-7000-8000-000100000057,019a0000-0000-7000-8000-000100000058,019a0000-0000-7000-8000-000100000059,019a0000-0000-7000-8000-00010000005a,019a0000-0000-7000-8000-00010000005b,019a0000-0000-7000-8000-00010000005c,019a0000-0000-7000-8000-00010000005d,019a0000-0000-7000-8000-00010000005e,019a0000-0000-7000-8000-00010000005f,019a0000-0000-7000-8000-000100000060,019a0000-0000-7000-8000-000100000061,019a0000-0000-7000-8000-000100000062,019a0000-0000-7000-8000-000100000063,019a0000-0000-7000-8000-000100000064}'::uuid[])))
Filter: ((name > 'review-prep-npm_tags-name-0999'::text) AND (npm_package_id = '019a0000-0000-7000-8000-0000000000b1'::uuid))
Rows Removed by Filter: 1000
Heap Blocks: exact=31
Buffers: shared hit=34
-> Bitmap Index Scan on npm_tags_p11_namespace_id_npm_version_id_idx (cost=0.00..47.86 rows=1333 width=0) (actual time=0.034..0.034 rows=2000 loops=1)
Index Cond: ((namespace_id = '019a0000-0000-7000-8000-00000000000a'::uuid) AND (npm_version_id = ANY ('{019a0000-0000-7000-8000-000100000001,019a0000-0000-7000-8000-000100000002,019a0000-0000-7000-8000-000100000003,019a0000-0000-7000-8000-000100000004,019a0000-0000-7000-8000-000100000005,019a0000-0000-7000-8000-000100000006,019a0000-0000-7000-8000-000100000007,019a0000-0000-7000-8000-000100000008,019a0000-0000-7000-8000-000100000009,019a0000-0000-7000-8000-00010000000a,019a0000-0000-7000-8000-00010000000b,019a0000-0000-7000-8000-00010000000c,019a0000-0000-7000-8000-00010000000d,019a0000-0000-7000-8000-00010000000e,019a0000-0000-7000-8000-00010000000f,019a0000-0000-7000-8000-000100000010,019a0000-0000-7000-8000-000100000011,019a0000-0000-7000-8000-000100000012,019a0000-0000-7000-8000-000100000013,019a0000-0000-7000-8000-000100000014,019a0000-0000-7000-8000-000100000015,019a0000-0000-7000-8000-000100000016,019a0000-0000-7000-8000-000100000017,019a0000-0000-7000-8000-000100000018,019a0000-0000-7000-8000-000100000019,019a0000-0000-7000-8000-00010000001a,019a0000-0000-7000-8000-00010000001b,019a0000-0000-7000-8000-00010000001c,019a0000-0000-7000-8000-00010000001d,019a0000-0000-7000-8000-00010000001e,019a0000-0000-7000-8000-00010000001f,019a0000-0000-7000-8000-000100000020,019a0000-0000-7000-8000-000100000021,019a0000-0000-7000-8000-000100000022,019a0000-0000-7000-8000-000100000023,019a0000-0000-7000-8000-000100000024,019a0000-0000-7000-8000-000100000025,019a0000-0000-7000-8000-000100000026,019a0000-0000-7000-8000-000100000027,019a0000-0000-7000-8000-000100000028,019a0000-0000-7000-8000-000100000029,019a0000-0000-7000-8000-00010000002a,019a0000-0000-7000-8000-00010000002b,019a0000-0000-7000-8000-00010000002c,019a0000-0000-7000-8000-00010000002d,019a0000-0000-7000-8000-00010000002e,019a0000-0000-7000-8000-00010000002f,019a0000-0000-7000-8000-000100000030,019a0000-0000-7000-8000-000100000031,019a0000-0000-7000-8000-000100000032,019a0000-0000-7000-8000-000100000033,019a0000-0000-7000-8000-000100000034,019a0000-0000-7000-8000-000100000035,019a0000-0000-7000-8000-000100000036,019a0000-0000-7000-8000-000100000037,019a0000-0000-7000-8000-000100000038,019a0000-0000-7000-8000-000100000039,019a0000-0000-7000-8000-00010000003a,019a0000-0000-7000-8000-00010000003b,019a0000-0000-7000-8000-00010000003c,019a0000-0000-7000-8000-00010000003d,019a0000-0000-7000-8000-00010000003e,019a0000-0000-7000-8000-00010000003f,019a0000-0000-7000-8000-000100000040,019a0000-0000-7000-8000-000100000041,019a0000-0000-7000-8000-000100000042,019a0000-0000-7000-8000-000100000043,019a0000-0000-7000-8000-000100000044,019a0000-0000-7000-8000-000100000045,019a0000-0000-7000-8000-000100000046,019a0000-0000-7000-8000-000100000047,019a0000-0000-7000-8000-000100000048,019a0000-0000-7000-8000-000100000049,019a0000-0000-7000-8000-00010000004a,019a0000-0000-7000-8000-00010000004b,019a0000-0000-7000-8000-00010000004c,019a0000-0000-7000-8000-00010000004d,019a0000-0000-7000-8000-00010000004e,019a0000-0000-7000-8000-00010000004f,019a0000-0000-7000-8000-000100000050,019a0000-0000-7000-8000-000100000051,019a0000-0000-7000-8000-000100000052,019a0000-0000-7000-8000-000100000053,019a0000-0000-7000-8000-000100000054,019a0000-0000-7000-8000-000100000055,019a0000-0000-7000-8000-000100000056,019a0000-0000-7000-8000-000100000057,019a0000-0000-7000-8000-000100000058,019a0000-0000-7000-8000-000100000059,019a0000-0000-7000-8000-00010000005a,019a0000-0000-7000-8000-00010000005b,019a0000-0000-7000-8000-00010000005c,019a0000-0000-7000-8000-00010000005d,019a0000-0000-7000-8000-00010000005e,019a0000-0000-7000-8000-00010000005f,019a0000-0000-7000-8000-000100000060,019a0000-0000-7000-8000-000100000061,019a0000-0000-7000-8000-000100000062,019a0000-0000-7000-8000-000100000063,019a0000-0000-7000-8000-000100000064}'::uuid[])))
Buffers: shared hit=3
Planning:
Buffers: shared hit=3
Planning Time: 0.311 ms
Execution Time: 0.545 msTimings: planning 0.311ms, execution 0.545ms, total 0.856ms.
Process notes
- Working-plan mode by operator instruction: no
docs/plans/plan MR. 9866a3f2dis the branch's one--no-verifycommit (test-first, red by design). The bypass also skipped gitlint on its body, whose table rows exceed the 80-column limit, and squash discards that body. Three tests it names landed green in4e1de9454.DistTagNames.MarshalJSONis the package's first marshaler. Its error wrap is on a call that cannot fail, becausewrapcheckis on.- No new S17 acceptance criterion. The AC list is one auto-numbered list cited by number, and the resource table under "Artifact resources" is the field's normative home.
- No configuration, Bruno, or scenario statement owed: no knob, no operation added, and the e2e catalog gained two rows. Its Usage-data row sits after the lifecycle block because the fan-out appends at section end, a nit to fold once the sibling e2e MRs land.
- At
6e037cad1, a dupl-plus-nolintlint run over the two touched packages reported 30//nolint:dupldirectives that suppress nothing.0d895cac4drops the one in a file this MR touches, pre-existing atnpm_tags_test.go:301, and shrinks the doc comment above it to the test-file cap. The other 29 sit in files this MR does not touch and wait on Trim the //nolint:thelper tokens that suppress ... (#682) • Pawel Rozlach • 19.4.
Context for LLM agents
Rationale
- Probe-then-500 when a package holds more than 1,000 tags. Rejected because
npm.max_tags_per_packageis a documented tunable, so a supported setting would break the versions list for every tag-heavy package. Chosen: a name-keyset loop, one statement per 1,000 tags. - A
LATERALsubquery per version row. Rejected on the profile indocs/dev/database-query-patterns.md: 3x the plan work and 44x the buffers of a batched read. DistTags: []string{}in each of the four mappers. Rejected because a fifth mapper can forget it and the contract sweep covers only the kinds it enumerates. Chosen: oneMarshalJSONon the field type.REPEATABLE READaround the two statements. Rejected because it pins a pool connection across the page read where no other management read does, for a skew the client-side join already had.
Non-goals
- Container manifest
tags. Owned by the sibling issue: Container manifest reads for the UI: detail by ... (#1150) • Hayley Swimelar. - A per-version dist-tag count. The names are the column, and their length is the count.
- The
(namespace_id, npm_package_id, npm_version_id, name)index. A follow-up if a raised cap makes the Sort dominate. - A validated ceiling on
npm.max_tags_per_package. A configuration change, not this MR's.
Closes #1153 (closed)