fix(maven): execute the hosted last_downloaded_at access bump

Summary

B1 from the closed-beta SQL review: MavenPackageStore.BumpAccessTimestamps built its jet UPDATEs but never executed them — a documented no-op stub with tests asserting the no-op. The hosted Maven access signal S10 promises has therefore never been written: the management API serializes null last_downloaded_at forever, and ADR-010 retention, when it ships, would treat busily-accessed artifacts as untouched. Nothing errored, which is exactly why it survived.

  • Datastore (internal/datastore/maven_packages.go): the method now runs the two UPDATEs on the passed runner — the package row always, the version row when a version resolved — with the existing guard set and two new instrumented query labels. Both statements filter soft_deleted_at IS NULL (matching the ten sibling last_downloaded_at sites) and write GREATEST(last_downloaded_at, NOW()) only when the row is null or older than mavenAccessBumpWindow (one hour), so a resolved GET / HEAD / 304 records the access at most once per row per hour instead of paying two non-HOT UPDATEs per request — the container-remote sibling's freshness pattern. Zero RowsAffected (row absent, soft-deleted, or already fresh) is a no-op, not an error, and the error strings carry no row identifiers. TODO(s18-buffered-counters) swap markers sit directly at both UPDATE sites (the repo's invariant scan classifies them).
  • Format layer (internal/format/maven/download.go, buffered.go): bumpAccess dispatches through bufferedUpdate off the request path under its own hosted_last_downloaded_at buffered label, split from the remote rows' last_downloaded_at label so a hosted download flood cannot shed the remote cache rows' retention bumps. The per-label quotas are asymmetric and sum to the global cap of 64: downloads_count and the remote last_downloaded_at bump keep 20 in-flight slots each (the loss-sensitive columns — a shed downloads_count is permanently lost and the remote bump is the signal this split protects), while the windowed hosted bump and the version-size recompute run 12 each. The new column value is pinned and budgeted in the cardinality tables. bumpAccess carries no TODO(buffered) marker of its own; the markers that do exist sit at the top of buffered.go and on the in-flight cap.
  • Adapter (cmd/artifact-registry): BumpAccessTimestamps joins the pool-resolved set beside the other live bumps; MarkRepoLastUpdated remains the only stub in its list.

e2e catalog: no row asserts a null last_downloaded_at for hosted rows (the field was always serialized, just never populated), so no scenario is added or affected.

Diff size

1002 changed lines across 23 files versus the merge base (450 insertions, 552 deletions). Guardrail 18 asks for a split or a justification past 500 reviewable LOC; no split, because the review-response commits (the AppSec sub-quota finding and the review pass below) addressed this MR's own change and had to land on its branch — splitting mid-review would strand them in a second MR that cannot merge before this one. The comment-only share of the diff is the comment-caps ratchet: every touched comment block must shrink to its per-site cap, which is why the prose-heavy files carry large deletions with no behavior change. LOC by file group:

Group Files Changed lines
internal/datastore maven_packages.go, maven_remote_cache.go, query_names.go, 4 test files 330
internal/format/maven buffered.go, download.go, upload_emit_dispatch.go, metrics.go, 6 test files 578
internal/metrics cardinality.go, column_budget_test.go 76
cmd/artifact-registry wire_maven_adapter_test.go 13
docs/dev observability.md, remote-read-budgets.md 5

Governing ADRs

  • ADR-010 (data retention): this MR makes the access signal it consumes real for hosted Maven; the one-hour write window sits two orders inside ADR-010's day-granularity reading; no deviation.
  • ADR-007 / ADR-009: no schema or URL changes.

Testing

  • Test-first: test(maven): pin the executed hosted access bump (red until the implementation) — datastore unit (execution counts, error propagation), datastore integration vs real Postgres (version-level stamps both rows, package-level stamps the package only, soft-deleted rows are not stamped, a second bump within the window does not advance the row), and an internal format test asserting the dispatch goes through the bufferedUpdate seam (label counter), with the external suite's synchronous reads migrated to the wait-hook pattern.
  • The access-bump table pins the nil-versus-version decision (package-level metadata resolve) and the 206/416 range shapes; the abandoned-transfer and over-long delivery tests recover their per-request bump assertions.
  • Full unit tree green (go test ./...), datastore integration suite green (71.8s), golangci-lint clean, comment-caps clean.
  • Live: rebuilt driver instance — GET a hosted file → both last_downloaded_at columns stamped (verified via psql), dispatch metric result="ok", driver smoke 72/72.

Environment note

Landing this MR surfaced a corrupted shared object store (a concurrent process repacked/pruned mid-session, which also pruned blobs referenced only by worktree indexes). Repaired by git fetch --refetch; git fsck is clean for all origin-reachable and branch-reachable objects. A local repair to homebrew git 2.55 (missing git-submodule) is also in place.

Related to #971 (closed)

Edited by Moaz Khalifa

Merge request reports

Loading
Loading