Skip billing emission on offline-licensed instances

What does this MR do and why?

On an air-gapped self-managed instance (offline cloud licence), every Secrets Manager billing event is still sent to the billing collector unless local_billing_persistence is on. That flag ships with default_enabled: false. This affects secrets_read on every allowed read, in SecretsManagement::AuditLogWorker, and the nightly secrets_stored once per root group. Each attempt holds a billing emitter thread (Gitlab::Tracking::BillingAuthEmitter, an AsyncEmitter) for the 30 s connect timeout, then fails with Net::OpenTimeout and a failed to be reported to collector error log line.

This MR adds an emission_enabled? hook to Gitlab::BillingEvents::Client:

  • CE: always true, so nothing changes.
  • EE: false on a self-managed instance whose licence is offline_cloud_license?, whatever the state of the feature flag. SaaS is excluded by the same realm guard local_persistence_enabled? uses.

Resulting behaviour on an offline-licensed instance:

local_billing_persistence Before After
on persisted locally persisted locally (unchanged)
off emitted, emitter thread held 30 s, error log not emitted, info log billing event emission skipped

The internal event (usage_billing_event) is tracked in every case. The feature flag and its rollout (#628679) are not changed.

Resolves https://gitlab.com/gitlab-org/gitlab/-/issues/630795

Out of scope / follow-ups

  • Legacy licences (issue item 2): still emit. The test suite's default licence is a legacy one, and GDKs often run one too, so skipping them would change behaviour much more widely. The Secrets Manager add-on is provisioned through cloud licensing, so I expect a legacy instance not to emit Secrets Manager events at all. Not verified end to end.
  • Collector connect timeout (issue item 3): a connected instance with blocked egress still has each attempt hold an emitter thread for 30 s and log an error. That needs its own change.

Cost

None: License.current is cached, and the check only runs on the non-local path.

How to set up and validate locally

  1. Use an offline cloud licence, or stub License.current.offline_cloud_license? to return true in a Rails console.
  2. Keep Feature.disable(:local_billing_persistence) and Feature.enable(:billing_event_tracking).
  3. Gitlab::BillingEvents::Client.track_billing_event(event_type: 'secrets_read', category: 'Test', unit_of_measure: 'request', quantity: 1, namespace: Group.first)
  4. The call returns immediately. log/application_json.log shows BillingEvents: billing event emission skipped, and no Snowplow billing request is made.

Specs

ee/spec/lib/ee/gitlab/billing_events/client_spec.rb:

  • #emission_enabled?: offline licence, with the flag on and off, returns false. Returns true for a non-offline licence, for no licence, and on SaaS.
  • With the flag off on an offline-licensed instance: neither emits nor persists, for both the counter and snapshot entry points. Logs the skip and still tracks the internal event.

I undid each production change separately, the elsif, the EE override and the SaaS guard, and confirmed that a spec fails for each.

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist.

🤖 Generated with Claude Code

Edited by Dmytro Biryukov

Merge request reports

Loading
Loading