feat(maven): sidecar synthesis from a warm remote parent (S14 Step 12)
What
Lands checksum-sidecar synthesis for an already-cached Maven remote parent —
Step 12 of the S14 Maven remote vertical slice. serveRemoteSidecar replaces
Step 10's 501 stub: it is a pure column read against Step 8/9's remote
cache row, never consulting the parent's freshness and never issuing an
upstream request. An uncached parent still returns 501 until Step 16 lands
the cold-parent fill.
The digest-selection table sidecar_checksum.go already held for the hosted
PUT path (checksumSpecs, keyed by SidecarAlgorithm) is retargeted from
MavenFile onto a small digest carrier both MavenFile and the remote cache
row project into, and gains the SidecarMD5 entry it lacked. sidecarBody
collapses its parallel switch onto that table and keeps the 404/500 error
writes: the NULL-md5 404, and the errChecksumNoColumn /
errChecksumColumnLength 500 mapping with its sentinel logging.
One table now serves hosted GET synthesis, remote GET synthesis, and PUT
validation, so a hosted and a remote parent holding the same digests
synthesize byte-identical 200 bodies for all four extensions. The 200 body
is the scope of that claim: on a conditional request the two kinds diverge,
because serveSidecar (hosted) never reads If-None-Match and always reaches
its 200, while serveRemoteSidecar answers 304 with no body for a matching
validator. Same URL shape, behavior forking on kind — a deliberate divergence,
documented at sidecar.go's hosted-path comment.
Alongside the synthesis path:
handler.gogainsRemoteCacheRowFinder,Deps.RemoteCache, andHandler.remoteCache— a coordinate-keyed lookup seam, since threading Step 8's path-stringRemoteCacheStorewrapper throughNewDispatchHandlerwould force a re-parse of a coordinate this handler already holds parsed. It is a consumer-side interface in the spirit ofDownloadStore/DigestStore, but not the same shape: those two are sub-interfaces cut from the singleStoreaggregate, whileRemoteCacheRowFinderis a separately wired optionalDepsfield with its own datastore concrete. An additiveDepsfield, not a signature change.wire_maven.gowiresDeps.RemoteCachetodatastore.NewMavenRemoteCacheStore(client), which satisfiesRemoteCacheRowFinderwith no adapter.remoteLookupCoordsis the one constructor both cache readers use (RemoteCacheStore.LookupandserveRemoteSidecar), so the two readers' coordinate mappings cannot drift asMavenRemoteLookupCoordsgrows.handler_test.gonarrows four tests that asserted Step 10's now-superseded501-stub behavior for the checksum-sidecar class; the artifact/metadata rows they otherwise cover are untouched.
Tests
Every cited acceptance clause has a named asserting test:
| Acceptance clause | Test |
|---|---|
All four extensions synthesize (incl. NULL md5 → 404) |
TestServeRemoteSidecar_AllFourExtensions (one matrix over parent class × extension), TestServeRemoteSidecar_MD5NullColumn404 |
FIPS .md5 short-circuit, non-md5 unaffected |
TestServeRemoteSidecar_FIPSShortCircuit, TestServeRemoteSidecar_FIPSOnNonMD5StillSynthesizes, TestServeRemoteSidecar_FIPSGate_DirectCall_422 |
| No upstream sidecar fetch | Structural: serveRemoteSidecar's row.Found path never references an upstream client; the integration suite wires none |
| No counter mutation | TestServeRemoteSidecar_NeverBumpsCounters |
| Sidecar conditional request (weak ETag, 304 + validator, GET and HEAD) | TestServeRemoteSidecar_ConditionalRequest304, TestServeRemoteSidecar_NonMatchingIfNoneMatch200, TestServeRemoteSidecar_IfNoneMatchAcrossFieldLines304, unit-backed by TestIfNoneMatchSatisfied / TestWeakETagEqual |
HEAD parity |
HEAD subtests inside the extension matrix (both parent classes, full GET header set), the FIPS tests, and the NULL-md5 test above |
| Cache-generation consistency (artifact and metadata parents) | TestServeRemoteSidecar_CacheGenerationConsistency_ArtifactParent / _MetadataParent |
Uncached parent still 501 |
TestServeRemoteSidecar_UncachedParent501 |
| Hosted and remote parents synthesize byte-identical bodies | TestServeRemoteSidecar_HostedAndRemoteSameDigests_ByteIdentical (HTTP), TestSidecarSynthesis_HostedAndRemoteCarriersAgree (DB-free) |
| Table-driven digest selection, all four algorithms plus every error sentinel | TestSidecarSynthesis, TestChecksumSpecsFlags |
The existing sidecar_test.go and sidecar_extensions_test.go are the
refactor's regression guard for the retargeted table and are unchanged apart
from one stale comment.
The two SQL statements this MR first makes production-reachable already carry
partition-pruning EXPLAIN coverage from Step 8
(TestMavenRemoteCacheStore_VersionLevelStmt_PrunesToOnePartition and
_PackageLevelStmt_PrunesToOnePartition in
internal/datastore/maven_remote_cache_explain_integration_test.go). Those
assert pruning, not index choice; adding an index-scan assertion would touch
Step 8's files and is left to the datastore owner.
Size
~2,675 changed lines, over the 500 ceiling in
development-model.md, justified here per that
document rather than split. Measured at 4e801a4a after three review rounds
(+2,225/−450 against base 73ab9b62):
| Part | Lines |
|---|---|
| Test churn | ~1,605 |
| Production Go | ~870 |
| Plan text | ~180 |
CLAUDE.md |
20 |
The integration suite alone is 754 lines, driven by the acceptance matrix:
four extensions against two parent classes, GET and HEAD, FIPS on and off.
Of the production Go, much of sidecar_checksum.go's share is comment
rewriting for the carrier retarget rather than new logic. A split is available —
the carrier plus the checksumSpecs retarget and the sidecarBody collapse in
one MR, the synthesis path stacked on it — and is not taken here: the collapse's
motivation is only checkable with its second caller in the same diff, and the
overage is test churn that a split redistributes rather than reduces. The plan's
Step 12 size row records the same breakdown.
Rollout
During a rolling deploy the same kind=2 sidecar URL answers 501 on an old
pod and 200/304/404/422 on a new one, and a client can take an ETag
from a new pod and send If-None-Match to an old one, getting 501 rather
than 304. Both self-heal on the next request and the direction is
501 → real behavior, so no client contract breaks; flagged so a mixed-status
dashboard during the deploy window reads as expected.
Nothing writes maven_remote_files yet and no endpoint creates a kind=2
Maven repository, so the warm branch is unreachable in production until
Steps 13 to 15 land. That reachability rests on one production gate: the kind
check in internal/managementapi/create.go, which rejects kind != hosted
with 422 before the datastore, while RepositoryStore.Create,
insertFormatChild, and the check_repositories_kind constraint all admit
kind = 2. The resulting merge-order obligation — creation or a backfill must
not land ahead of Steps 14 to 16 — is recorded in the plan's
Reachability of the remote read paths
section and tracked in
#604 (closed), where the creation work will see it.
Review follow-ups
- Both automated-review findings are fixed in
ddd1d421: the self-contained FIPS gate inserveRemoteSidecar(AppSec) and the ETag threaded intowriteSidecarOKinstead of recomputed (Duo). - A branch review raised a decision on
weakETagEqualrenormalizing the response ETag once perIf-None-Matchcandidate. Deferred deliberately so the fix covers the Maven and npm copies together rather than drifting them: #569.
E2E scenario impact
None. No Maven e2e catalog exists yet (docs/testing/e2e/ holds only
README.md, docker.md, and oci.md); Step 18's hermetic harness is the
automated coverage meanwhile, per the plan's Testing Strategy.
The Maven conformance suite does exist and runs on this MR:
conformance:maven:s3-garage is anchored on internal/format/maven/**/* and
cmd/artifact-registry/**/*, both of which this MR touches, and it exercises
the hosted sidecar GET path this MR refactors. It has no kind=2 target for the
new remote path: its fixture scripts/conformance/maven-provision/main.go seeds
the repository row by direct INSERT with kind=0, so the provisioner is what a
remote conformance case has to change first, not the 422 in
internal/managementapi/create.go that the Rollout section names.
Related to #286 (closed)