feat(npm): metadata-file write methods (npm hosted step 12, part 2/5)

This change is split into 5 stacked MRs to keep each within review size. Each part targets the previous one (part 1 targets main); review and merge bottom-up, part 1 first.

Part 2 of 5 of docs/plans/2026-05-11-npm-hosted.md — Step 12: packument cache rebuilder (in-process).

Adds the simple npm_metadata_files write paths used by the rebuilder: ForceExpireNpmMetadata (sets expires_at = NOW() to invalidate all kinds) and UpsertNpmMetadataFile (insert-or-update against a caller-supplied attachment), sharing the private upsertNpmMetadataFileStmt builder. Step 5's scope listed these methods, but they did not ship then; the rebuild service is their consumer, so they land here.

Related to #130 (closed)

Stacked MRs (review/merge bottom-up)

Database evidence

Static index analysis (not EXPLAIN-verified). Docker/Colima is unavailable in this environment, so there is no live database and no executed plan. Each query's WHERE / conflict-target columns are matched against the declared indexes, primary keys, and partition keys in internal/datastore/migrations/structure.sql and ADR 007. No migration ships on this branch (npm_metadata_files exists in a pre-existing migration), so migration timing analysis is n/a.

Relevant schema facts:

  • npm_metadata_files is PARTITION BY HASH (namespace_id) with composite primary key (id, namespace_id).
  • unique_npm_metadata_files_ns_id_pkg_id_kind is a unique btree index on (namespace_id, npm_package_id, kind).

NpmMetadataFileStore.ForceExpireNpmMetadata (UPDATE)

UPDATE npm_metadata_files SET expires_at = NOW() WHERE namespace_id = $1 AND npm_package_id = $2. The (namespace_id, npm_package_id) predicate is the two-column prefix of unique_npm_metadata_files_ns_id_pkg_id_kind (namespace_id, npm_package_id, kind), so the index covers the lookup (it intentionally matches all three kind rows for the package by leaving kind unconstrained). namespace_id is present, so the planner prunes to the single namespace hash partition. No anomalies under static analysis.

NpmMetadataFileStore.UpsertNpmMetadataFile via upsertNpmMetadataFileStmt (INSERT ... ON CONFLICT DO UPDATE)

INSERT INTO npm_metadata_files (...) VALUES (...) ON CONFLICT (namespace_id, npm_package_id, kind) DO UPDATE SET blob_storage_attachment_id = ..., blob_sha256 = ..., expires_at = .... The conflict target (namespace_id, npm_package_id, kind) matches unique_npm_metadata_files_ns_id_pkg_id_kind column-for-column, which is the index PostgreSQL requires to arbitrate the conflict. namespace_id is the leading conflict-target column and the partition key, so the conflict probe prunes to the single namespace hash partition. No anomalies under static analysis.

Edited by David Fernandez

Merge request reports

Loading
Loading