[FF] local_billing_persistence — persist billable usage locally on air-gapped instances
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
Summary
Roll out local billable usage persistence currently behind the local_billing_persistence feature flag.
- DRI: @vij
- Team Slack channel:
#g_fulfillment_cost_management
Important
This flag cannot be rolled out on GitLab.com. It gates on License.current&.offline_cloud_license?, so it is inert on .com, on Dedicated, and on any connected instance without an offline licence. The standard chatops percentage rollout does not apply, and neither do the gprd Kibana links or dashboards.gitlab.net — an air-gapped instance sends us nothing by definition. The sections below are adapted accordingly.
Depends on billing_event_tracking
This flag chooses where a billable event goes; it does not decide whether one is captured at all. Gitlab::BillingEvents::Client#record_usage checks billing_event_tracking on its first line and returns before reaching either branch, so the two are nested rather than independent — the local-persistence path only comes into play once billing event capture is already on.
billing_event_tracking covers the whole billing events API, arrived in %19.2 via !240688 (merged), and is owned by groupanalytics instrumentation. It is still default-off, so both need to be on for anything to be recorded:
Feature.enable(:billing_event_tracking)
Feature.enable(:local_billing_persistence)An administrator who enables only this flag sees no rows and no error, so it is worth spelling out in any enablement instructions.
- Check in with groupanalytics instrumentation on
billing_event_trackingsequencing, so the two rollouts line up
What could go wrong?
Emission is replaced, not supplemented. On an offline-licensed instance the client stops emitting to the billing collector and writes to billable_usage_daily_aggregates instead. That is the intent for a genuinely air-gapped instance, which was losing the events anyway. It is not obviously right for a connected instance running an offline licence — that instance emits successfully today and would switch to local rows requiring a manual export. Contained while the flag is opt-in per instance; it must be settled before default_enabled is flipped.
Failures are silent. track_billing_event swallows exceptions into error tracking, so a misconfigured instance looks identical to a working one from the caller's side.
Two known limitations, both recorded on !249324 (merged): a gauge cannot report zero, and accumulated quantity is not checked against the Iglu export ceiling.
Verification
There is no central telemetry. Verify on the instance:
# Is the path actually live?
Gitlab::BillingEvents::Client.local_persistence_enabled? # => true
# Are rows landing?
Utilization::BillableUsage::DailyAggregate.order(:id).pluck(
:usage_date, :event_type, :feature_qualified_name, :root_namespace_id,
:operation_type, :quantity, :events_count)In the instance's own logs (not gprd):
BillingEvents: aggregate not recorded— the write was rejected; the usual cause is a missingmetadata[:feature_qualified_name]BillingEvents: billing event tracked— should be absent; its presence means emission happened instead of local persistenceBillingEvents: internal event tracked— should be present on both paths
Rollout
- Enable on an offline-licensed test instance and confirm rows land for
secrets_read(counter) andsecrets_stored(snapshot) - Confirm a repeated snapshot supersedes rather than sums, since that is the behaviour unique to this change
- Enable for one willing air-gapped customer with Support, and verify the exported figures against their own expectations
- Decide with Product whether
default_enabled: trueships in a release, or whether this stays administrator-enabled until the export flow (&23205) lands
Before defaulting on
- Resolve the connected-instance-with-offline-licence case above
- Export flow (&23205) and ingestion (&23206) are ready, otherwise instances accumulate rows nobody can submit
- Retention decided — rows are currently kept indefinitely (#18555)
- Administrator-facing documentation exists; the current page is developer documentation only
Rollback
Per instance, in a Rails console:
Feature.disable(:local_billing_persistence)Emission resumes immediately. Rows already written stay in place and remain exportable; nothing needs unwinding.
Cleanup
Remove the flag once the export flow is shipped and the behaviour is stable — see cleaning up. Remove the flag and its YAML definition, then open a follow-up Feature Flag Cleanup issue.