Loading
feat(usagedata): wire the tracker into the composition root (S23 Step 3)
What
S23 Step 3 of the usage-data foundation plan: wire the internal/usagedata tracker (Step 2, !912 (merged)) into the composition root.
cmd/artifact-registry/wire_usagedata.gobuilds the tracker fromcfg.UsageDataand registers it as the firstapp.Component, so the LIFO drain stops it last: in-flight handlers and draining job tiers can still emit, and the tracker flushes after they finish. Disabled config wires the same no-op tracker unconditionally, keeping one code path and no nil checks for later tiers.- Tracker metrics register on the base registerer (the families bake the service prefix themselves; the prefixing wrapper would double it).
docs/dev/observability.mdgains the 11 S23 metric-family rows and records the base-registerer exception. deriveAppShutdownTimeoutbecomes variadic and the call site adds the tracker's 5s drain slice to the shared app shutdown budget.assembledApp.UsageDataexposes the tracker as the seam Step 5's handlers will consume.
Stacked on !912 (merged) (Step 2) — this MR targets jdrpereira/s23-usage-data-foundation-step-2 and must merge after it; GitLab retargets to main automatically when !912 (merged) merges.
Spec coverage
Spec: docs/specs/S23-usage-data-collection.md
This MR owns AC-10 and the composition-level halves of AC-1/AC-7/E-4. Package-level rows are owned by the earlier steps of the same plan: Step 1 (internal/config, !910 (merged)) and Step 2 (internal/usagedata, !912 (merged)). AC-9 lands with Step 5.
Acceptance criteria
| # | Criterion | Tests |
|---|---|---|
| AC-1 | Disabled or absent section: Track/lifecycle succeed, no HTTP | Step 2: TestNew_Disabled_Noop; this MR (composition): TestWireUsageData_DisabledBoot |
| AC-2 | Track produces a collector POST carrying both context schemas | Step 2: TestTracker_Track_EmitsBothContexts |
| AC-3 | ar_instance_version equals app.Version, not settable |
Step 2: TestTracker_Track_InstanceVersionFromBuildInfo, TestAttrs_FieldSurface |
| AC-4 | correlation_id equals the request's correlation ID |
Step 2: TestTracker_Track_EmitsBothContexts |
| AC-5 | Out-of-enum kind/upstream drops + reason="validation" counter |
Step 2: TestTracker_Track_InvalidEnum_DropsEvent |
| AC-6 | Forced gitlab_standard failure drops through the same path |
Step 2: TestTracker_Track_GitLabStandardFailure_DropsEvent |
| AC-7 | Shutdown delivers queued events; bounded when unreachable | Step 2: TestTracker_Shutdown_DrainsQueuedEvents, TestTracker_Shutdown_UnreachableCollector_ReturnsWithinBudget; this MR (composition): TestWireUsageData_TrackerRegisteredAsComponent |
| AC-8 | Track safe under concurrent use (race detector) | Step 2: TestTracker_Track_ConcurrentEmission, TestTracker_ConcurrentTrackAndShutdown |
| AC-9 | Repository create emits exactly one first event | Step 5 (first event); not in this MR |
| AC-10 | Emitter metrics under ..._snowplow_events_*; dropped counter on the service registry |
This MR: TestWireUsageData_EnabledBootRegistersServiceMetrics, TestWireUsageData_MetricsEndpointServesUsageDataSeries; Step 2: TestNew_EmitterMetricsUseServicePrefix |
Error cases
| # | Condition | Tests |
|---|---|---|
| E-1 | Invalid config (bad URL, missing fields, bad enums) fails boot | Step 1: TestLoad_UsageData_ValidationRejections |
| E-2 | Collector unreachable: retry/backoff, bounded queue, Track never blocks | LabKit-owned emitter internals; bounded-shutdown half in Step 2: TestTracker_Shutdown_UnreachableCollector_ReturnsWithinBudget |
| E-3 | Context validation failure: drop + counter + log | Step 2: TestTracker_Track_InvalidEnum_DropsEvent, TestTracker_Track_UnknownPropKey_DropsEvent |
| E-4 | Track after Shutdown: no-op, reason="stopped" counter |
Step 2: TestTracker_TrackAfterShutdown_DropsWithStoppedReason; this MR (composition): TestWireUsageData_TrackerRegisteredAsComponent |
| E-5 | Process exit with queued events: events lost | Accepted data loss per ADR-012; deliberately not asserted |
Security considerations
| # | Concern | Tests |
|---|---|---|
| S-1 | No tokens, credentials, or raw PII; identity limited to org ID | Step 2: TestTracker_Track_NoIdentityOrRailsScopedFields |
| S-2 | Props allow-listed against the ADR-012 catalog | Step 2: TestValidateProps, TestTracker_Track_UnknownPropKey_DropsEvent |
| S-3 | Attrs cannot carry version, correlation ID, or identity | Step 2: TestAttrs_FieldSurface, TestTracker_Track_InstanceVersionFromBuildInfo |
| S-4 | TLS enforced for the collector outside development | Step 1: TestLoad_UsageData_ValidationRejections, TestLoad_UsageData_HTTPAllowedInDevelopment |
| S-5 | enabled = false: nothing leaves the process |
Step 2: TestNew_Disabled_Noop; this MR (composition): TestWireUsageData_DisabledBoot |
Notes
- The first commit is the documented test-first
--no-verifycarve-out; every other commit ran the full hook chain.
Related to #272 (closed)