From 9b19176347e4368b813626f1c08d0641f2ecc9d7 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 29 Sep 2026 13:35:17 -0400 Subject: [PATCH 1/5] resourcemanager: EmitUsageValue and workflow usage constants; loop: CL_CAPABILITY_USAGE_ENABLED Groundwork for capability-centric workflow billing (CRE-6778): - ResourceManager.EmitUsageValue emits a METER_ACTION_USAGE record from a *big.Int so chain-write capabilities can report gas fees in wei without truncation. - resourcemanager/workflow_usage.go pins the producer side of the billing contract: record type cre:workflow, resource types cre:workflow:compute and cre:workflow:gas:, ResourceId ":", emitting service names and resource pool. Matches the consumer in billing-platform-service (ConstructCapabilityUsageEventID). - loop.EnvConfig.CapabilityUsageEnabled (CL_CAPABILITY_USAGE_ENABLED) carries the new [Metering].CapabilityUsageEnabled node setting to LOOP capability plugins, independent of MeterRecordsEnabled. Co-Authored-By: Claude Fable 5.1 --- pkg/loop/config.go | 10 ++++ pkg/loop/config_test.go | 3 ++ pkg/resourcemanager/resourcemanager.go | 8 +++ pkg/resourcemanager/resourcemanager_test.go | 29 +++++++++++ pkg/resourcemanager/workflow_usage.go | 58 +++++++++++++++++++++ pkg/resourcemanager/workflow_usage_test.go | 24 +++++++++ 6 files changed, 132 insertions(+) create mode 100644 pkg/resourcemanager/workflow_usage.go create mode 100644 pkg/resourcemanager/workflow_usage_test.go diff --git a/pkg/loop/config.go b/pkg/loop/config.go index cf4d535639..376b1488d2 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -90,6 +90,7 @@ const ( envTelemetryLogCompressor = "CL_TELEMETRY_LOG_COMPRESSOR" envMeterRecordsEnabled = "CL_METER_RECORDS_ENABLED" envMeterSnapshotsEnabled = "CL_METER_SNAPSHOTS_ENABLED" + envCapabilityUsageEnabled = "CL_CAPABILITY_USAGE_ENABLED" envMeterProduct = "CL_METER_PRODUCT" envMeterTenant = "CL_METER_TENANT" envMeterNumericTenantID = "CL_METER_NUMERIC_TENANT_ID" @@ -207,6 +208,10 @@ type EnvConfig struct { TelemetryLogCompressor string MeterRecordsEnabled bool MeterSnapshotsEnabled bool + // CapabilityUsageEnabled enables per-capability workflow usage MeterRecords + // (compute, gas) for eventually consistent billing. Independent of + // MeterRecordsEnabled, which gates durable resource metering. + CapabilityUsageEnabled bool // MeterProduct / MeterTenant / MeterNumericTenantID / MeterEnvironment / // MeterZone / MeterNodeID are @@ -327,6 +332,7 @@ func (e *EnvConfig) AsCmdEnv() (env []string) { add(envTelemetryLogCompressor, e.TelemetryLogCompressor) add(envMeterRecordsEnabled, strconv.FormatBool(e.MeterRecordsEnabled)) add(envMeterSnapshotsEnabled, strconv.FormatBool(e.MeterSnapshotsEnabled)) + add(envCapabilityUsageEnabled, strconv.FormatBool(e.CapabilityUsageEnabled)) add(envMeterProduct, e.MeterProduct) add(envMeterTenant, e.MeterTenant) add(envMeterNumericTenantID, e.MeterNumericTenantID) @@ -649,6 +655,10 @@ func (e *EnvConfig) parse() error { if err != nil { return fmt.Errorf("failed to parse %s: %w", envMeterSnapshotsEnabled, err) } + e.CapabilityUsageEnabled, err = getBool(envCapabilityUsageEnabled) + if err != nil { + return fmt.Errorf("failed to parse %s: %w", envCapabilityUsageEnabled, err) + } e.MeterProduct = os.Getenv(envMeterProduct) e.MeterTenant = os.Getenv(envMeterTenant) diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index c128f82ec2..1b96beb1f3 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -89,6 +89,7 @@ func TestEnvConfig_parse(t *testing.T) { envTelemetryPrometheusBridgePrefixes: "foo,bar", envMeterRecordsEnabled: "true", envMeterSnapshotsEnabled: "false", + envCapabilityUsageEnabled: "true", envMeterProduct: "cre-mainline", envMeterTenant: "mainline", envMeterNumericTenantID: "42", @@ -230,6 +231,7 @@ var envCfgFull = EnvConfig{ TelemetryPrometheusBridgePrefixes: []string{"foo", "bar"}, MeterRecordsEnabled: true, MeterSnapshotsEnabled: false, + CapabilityUsageEnabled: true, MeterProduct: "cre-mainline", MeterTenant: "mainline", MeterNumericTenantID: "42", @@ -313,6 +315,7 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { assert.Equal(t, "foo,bar", got[envTelemetryPrometheusBridgePrefixes]) assert.Equal(t, "true", got[envMeterRecordsEnabled]) assert.Equal(t, "false", got[envMeterSnapshotsEnabled]) + assert.Equal(t, "true", got[envCapabilityUsageEnabled]) assert.Equal(t, "cre-mainline", got[envMeterProduct]) assert.Equal(t, "mainline", got[envMeterTenant]) assert.Equal(t, "42", got[envMeterNumericTenantID]) diff --git a/pkg/resourcemanager/resourcemanager.go b/pkg/resourcemanager/resourcemanager.go index 6a2f80d706..752867ad0d 100644 --- a/pkg/resourcemanager/resourcemanager.go +++ b/pkg/resourcemanager/resourcemanager.go @@ -52,6 +52,7 @@ package resourcemanager import ( "context" + "math/big" "strconv" "sync" "time" @@ -444,6 +445,13 @@ func (rm *ResourceManager) EmitUsage(ctx context.Context, identity ResourceIdent rm.emitRecord(ctx, identity, eventID, meteringpb.MeterAction_METER_ACTION_USAGE, NewUtilizationInt(quantity, fields)) } +// EmitUsageValue is EmitUsage for quantities that do not fit an int64, such as a +// transaction fee in wei. A nil value emits "0". Same event_id contract and +// fail-open semantics as EmitUsage. +func (rm *ResourceManager) EmitUsageValue(ctx context.Context, identity ResourceIdentity, eventID string, value *big.Int, fields UtilizationFields) { + rm.emitRecord(ctx, identity, eventID, meteringpb.MeterAction_METER_ACTION_USAGE, NewUtilizationBig(value, fields)) +} + // emitRecord builds and emits a single-utilization MeterRecord for action. // eventID is supplied by the caller and stamped on the utilization; it is never // generated here. An empty eventID means the producer failed to derive a diff --git a/pkg/resourcemanager/resourcemanager_test.go b/pkg/resourcemanager/resourcemanager_test.go index 824e2b2aae..8359377237 100644 --- a/pkg/resourcemanager/resourcemanager_test.go +++ b/pkg/resourcemanager/resourcemanager_test.go @@ -188,6 +188,35 @@ func TestEmitUsage_Action(t *testing.T) { assert.Equal(t, "usage:req-1", record.GetUtilizations()[0].GetEventId()) } +func TestEmitUsageValue_Action(t *testing.T) { + emitter := &fakeEmitter{} + rm := NewResourceManager(logger.Test(t), ResourceManagerConfig{ + MeterRecordsEnabled: true, + Emitter: emitter, + }) + + fee, ok := new(big.Int).SetString("123456789012345678901234567890", 10) // > MaxInt64 + require.True(t, ok) + rm.EmitUsageValue(t.Context(), testIdentity, "0xabc", fee, UtilizationFields{ + ResourceType: WorkflowGasResourceType(1), + ResourceID: "wf-1:exec-1", + OrgID: "org-1", + }) + rm.EmitUsageValue(t.Context(), testIdentity, "0xdef", nil, UtilizationFields{ResourceType: WorkflowGasResourceType(1), ResourceID: "wf-1:exec-2"}) + + require.Len(t, emitter.calls, 2) + var record meteringpb.MeterRecord + require.NoError(t, proto.Unmarshal(emitter.calls[0].body, &record)) + assert.Equal(t, meteringpb.MeterAction_METER_ACTION_USAGE, record.GetAction()) + require.Len(t, record.GetUtilizations(), 1) + assert.Equal(t, "123456789012345678901234567890", record.GetUtilizations()[0].GetValue()) + assert.Equal(t, "cre:workflow:gas:1", record.GetUtilizations()[0].GetResourceType()) + assert.Equal(t, "0xabc", record.GetUtilizations()[0].GetEventId()) + + require.NoError(t, proto.Unmarshal(emitter.calls[1].body, &record)) + assert.Equal(t, "0", record.GetUtilizations()[0].GetValue()) +} + // TestEmitDelta_EventIDIdenticalAcrossNodes proves the core cross-node contract: // two nodes fielding the SAME logical delta (identical eventID) emit the // identical event_id, while a distinct request (distinct eventID) is distinct. diff --git a/pkg/resourcemanager/workflow_usage.go b/pkg/resourcemanager/workflow_usage.go new file mode 100644 index 0000000000..907ea095cf --- /dev/null +++ b/pkg/resourcemanager/workflow_usage.go @@ -0,0 +1,58 @@ +package resourcemanager + +import ( + "errors" + "strconv" + "strings" +) + +// Workflow capability usage records: one METER_ACTION_USAGE MeterRecord per +// billable capability event of a workflow execution (compute duration, gas per +// chain write). The billing service turns each into a billing_records row with +// event id "cre:workflow:::", +// built from Utilization.ResourceId (":") and +// Utilization.EventId (the capability event id). Both must be identical on every +// node of the DON: they are the quorum and dedup key. +const ( + // WorkflowRecordType is the billing record type shared by all workflow + // capability usage resource types. + WorkflowRecordType = "cre:workflow" + + // ResourceTypeWorkflowCompute meters ordinary workflow compute in + // milliseconds. The capability event id is the workflow execution id. + ResourceTypeWorkflowCompute = WorkflowRecordType + ":compute" + + // ResourceTypeWorkflowGasPrefix is the prefix of the per-chain gas resource + // type; see WorkflowGasResourceType. Values are in the chain's native + // smallest unit (wei, lamports). The capability event id is the tx hash. + ResourceTypeWorkflowGasPrefix = WorkflowRecordType + ":gas:" + + // EmittingServiceChainWrite is the Identity.Service used by chain-write + // capabilities for gas usage records. + EmittingServiceChainWrite = "chain-write" + // EmittingServiceWorkflowEngine is the Identity.Service used by the + // workflow engine for compute usage records. + EmittingServiceWorkflowEngine = "workflow-engine" + + // WorkflowUsageResourcePool is the Identity.ResourcePool for workflow + // capability usage records. + WorkflowUsageResourcePool = "workflow_usage" +) + +var errWorkflowUsageResourceID = errors.New("workflow usage resource id: workflow id and execution id must be non-empty and contain no ':'") + +// WorkflowGasResourceType returns the gas resource type for a chain selector, +// e.g. "cre:workflow:gas:421614". +func WorkflowGasResourceType(chainSelector uint64) string { + return ResourceTypeWorkflowGasPrefix + strconv.FormatUint(chainSelector, 10) +} + +// WorkflowUsageResourceID returns the Utilization.ResourceId for a workflow +// execution: ":". Neither component may be empty or +// contain ':', since the consumer splits on the first ':'. +func WorkflowUsageResourceID(workflowID, executionID string) (string, error) { + if workflowID == "" || executionID == "" || strings.Contains(workflowID, ":") || strings.Contains(executionID, ":") { + return "", errWorkflowUsageResourceID + } + return workflowID + ":" + executionID, nil +} diff --git a/pkg/resourcemanager/workflow_usage_test.go b/pkg/resourcemanager/workflow_usage_test.go new file mode 100644 index 0000000000..c9ffc1afc7 --- /dev/null +++ b/pkg/resourcemanager/workflow_usage_test.go @@ -0,0 +1,24 @@ +package resourcemanager + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestWorkflowGasResourceType(t *testing.T) { + assert.Equal(t, "cre:workflow:gas:421614", WorkflowGasResourceType(421614)) + assert.Equal(t, "cre:workflow:compute", ResourceTypeWorkflowCompute) +} + +func TestWorkflowUsageResourceID(t *testing.T) { + id, err := WorkflowUsageResourceID("wf-1", "exec-1") + require.NoError(t, err) + assert.Equal(t, "wf-1:exec-1", id) + + for _, tc := range [][2]string{{"", "exec-1"}, {"wf-1", ""}, {"wf:1", "exec-1"}, {"wf-1", "exec:1"}} { + _, err := WorkflowUsageResourceID(tc[0], tc[1]) + assert.ErrorIs(t, err, errWorkflowUsageResourceID, "%q %q", tc[0], tc[1]) + } +} From 938825727e4acdbb2c37fcc8bbc1d69344365953 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Mon, 5 Oct 2026 14:37:01 -0400 Subject: [PATCH 2/5] loop: CapabilityUsageConfig, a usage ResourceManager config gated only by CapabilityUsageEnabled Review follow-up. ResourceManager drops every emit when its own MeterRecordsEnabled is false, so a usage manager built from MeteringConfig would be silently gated by the durable-metering flag. CapabilityUsageConfig returns a Config for a dedicated usage ResourceManager: records enabled iff CapabilityUsageEnabled, snapshots off, same emitter and deployment identity. Producers run that manager next to the durable one, so the two settings never share a gate. Co-Authored-By: Claude Fable 5.1 --- pkg/loop/config.go | 4 +++- pkg/loop/metering_config.go | 18 ++++++++++++++++++ pkg/loop/metering_config_test.go | 19 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 376b1488d2..10c4cd20e7 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -210,7 +210,9 @@ type EnvConfig struct { MeterSnapshotsEnabled bool // CapabilityUsageEnabled enables per-capability workflow usage MeterRecords // (compute, gas) for eventually consistent billing. Independent of - // MeterRecordsEnabled, which gates durable resource metering. + // MeterRecordsEnabled, which gates durable resource metering: producers + // run a dedicated ResourceManager for usage records built from + // CapabilityUsageConfig, so the two flags never share a gate. CapabilityUsageEnabled bool // MeterProduct / MeterTenant / MeterNumericTenantID / MeterEnvironment / diff --git a/pkg/loop/metering_config.go b/pkg/loop/metering_config.go index 2c82535fa0..bb662c8f29 100644 --- a/pkg/loop/metering_config.go +++ b/pkg/loop/metering_config.go @@ -34,3 +34,21 @@ func (e *EnvConfig) MeteringConfig(emitter resourcemanager.Emitter) resourcemana }, } } + +// CapabilityUsageConfig maps this EnvConfig to the resourcemanager.Config for +// a dedicated workflow capability usage ResourceManager (compute, gas +// records). It is gated by CapabilityUsageEnabled alone: MeterRecordsEnabled +// governs durable resource metering and must not switch billing records on or +// off, so the usage manager gets its own ResourceManagerConfig with records +// enabled iff CapabilityUsageEnabled, and snapshots always off (usage records +// have no level to snapshot). Same emitter and deployment identity as +// MeteringConfig. +func (e *EnvConfig) CapabilityUsageConfig(emitter resourcemanager.Emitter) resourcemanager.Config { + if e == nil { + return resourcemanager.Config{} + } + cfg := e.MeteringConfig(emitter) + cfg.MeterRecordsEnabled = e.CapabilityUsageEnabled + cfg.MeterSnapshotsEnabled = false + return cfg +} diff --git a/pkg/loop/metering_config_test.go b/pkg/loop/metering_config_test.go index 3478d2b428..58baa17d72 100644 --- a/pkg/loop/metering_config_test.go +++ b/pkg/loop/metering_config_test.go @@ -55,3 +55,22 @@ func TestEnvConfig_MeteringConfig(t *testing.T) { assert.Equal(t, "clp-cre-wf-zone-a-1", cfg.DeploymentIdentity.NodeID) }) } + +func TestEnvConfig_CapabilityUsageConfig(t *testing.T) { + t.Run("gated by CapabilityUsageEnabled only", func(t *testing.T) { + e := &EnvConfig{MeterRecordsEnabled: false, MeterSnapshotsEnabled: true, CapabilityUsageEnabled: true, MeterProduct: "cre", MeterNodeID: "n1"} + cfg := e.CapabilityUsageConfig(nil) + assert.True(t, cfg.MeterRecordsEnabled, "usage records on even though durable MeterRecordsEnabled is off") + assert.False(t, cfg.MeterSnapshotsEnabled, "usage records never snapshot") + assert.Equal(t, "cre", cfg.DeploymentIdentity.Product) + assert.Equal(t, "n1", cfg.DeploymentIdentity.NodeID) + }) + t.Run("off when CapabilityUsageEnabled is false regardless of MeterRecordsEnabled", func(t *testing.T) { + e := &EnvConfig{MeterRecordsEnabled: true, CapabilityUsageEnabled: false} + assert.False(t, e.CapabilityUsageConfig(nil).MeterRecordsEnabled) + }) + t.Run("nil receiver", func(t *testing.T) { + var e *EnvConfig + assert.Equal(t, resourcemanager.Config{}, e.CapabilityUsageConfig(nil)) + }) +} From 96be6dc8c2b934b818a4344b66f19cfe55ec85fc Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Mon, 5 Oct 2026 16:52:38 -0400 Subject: [PATCH 3/5] resourcemanager: workflow usage resource pools per the billing payload contract Identity.ResourcePool / ResourcePoolID follow the Billing <-> Decentralized stack contract: pool "cre:workflow:gas" with pool id "cre:workflow:gas:" for gas, "cre:workflow:compute" for both on compute. WithWorkflowUsagePool derives them from the resource type so producers cannot drift. Replaces the single "workflow_usage" pool. Co-Authored-By: Claude Fable 5.1 --- pkg/resourcemanager/workflow_usage.go | 25 +++++++++++++++++++--- pkg/resourcemanager/workflow_usage_test.go | 12 +++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/pkg/resourcemanager/workflow_usage.go b/pkg/resourcemanager/workflow_usage.go index 907ea095cf..105c7b4d2d 100644 --- a/pkg/resourcemanager/workflow_usage.go +++ b/pkg/resourcemanager/workflow_usage.go @@ -34,13 +34,32 @@ const ( // workflow engine for compute usage records. EmittingServiceWorkflowEngine = "workflow-engine" - // WorkflowUsageResourcePool is the Identity.ResourcePool for workflow - // capability usage records. - WorkflowUsageResourcePool = "workflow_usage" + // WorkflowGasResourcePool is the Identity.ResourcePool for gas usage + // records; ResourcePoolID is the fully qualified resource type + // (cre:workflow:gas:). See WithWorkflowUsagePool. + WorkflowGasResourcePool = WorkflowRecordType + ":gas" + // WorkflowComputeResourcePool is the Identity.ResourcePool for compute + // usage records; ResourcePoolID is the same value. + WorkflowComputeResourcePool = ResourceTypeWorkflowCompute ) var errWorkflowUsageResourceID = errors.New("workflow usage resource id: workflow id and execution id must be non-empty and contain no ':'") +// WithWorkflowUsagePool returns id with ResourcePool and ResourcePoolID set +// for a workflow usage resource type, per the billing payload contract: +// pool is the resource type without its chain selector +// ("cre:workflow:gas", "cre:workflow:compute") and pool id is the fully +// qualified resource type. +func WithWorkflowUsagePool(id ResourceIdentity, resourceType string) ResourceIdentity { + if strings.HasPrefix(resourceType, ResourceTypeWorkflowGasPrefix) { + id.ResourcePool = WorkflowGasResourcePool + } else { + id.ResourcePool = resourceType + } + id.ResourcePoolID = resourceType + return id +} + // WorkflowGasResourceType returns the gas resource type for a chain selector, // e.g. "cre:workflow:gas:421614". func WorkflowGasResourceType(chainSelector uint64) string { diff --git a/pkg/resourcemanager/workflow_usage_test.go b/pkg/resourcemanager/workflow_usage_test.go index c9ffc1afc7..d3eca29907 100644 --- a/pkg/resourcemanager/workflow_usage_test.go +++ b/pkg/resourcemanager/workflow_usage_test.go @@ -22,3 +22,15 @@ func TestWorkflowUsageResourceID(t *testing.T) { assert.ErrorIs(t, err, errWorkflowUsageResourceID, "%q %q", tc[0], tc[1]) } } + +func TestWithWorkflowUsagePool(t *testing.T) { + base := ResourceIdentity{Product: "cre", Service: EmittingServiceChainWrite} + gas := WithWorkflowUsagePool(base, WorkflowGasResourceType(421614)) + assert.Equal(t, "cre:workflow:gas", gas.ResourcePool) + assert.Equal(t, "cre:workflow:gas:421614", gas.ResourcePoolID) + assert.Equal(t, EmittingServiceChainWrite, gas.Service, "other fields untouched") + + compute := WithWorkflowUsagePool(base, ResourceTypeWorkflowCompute) + assert.Equal(t, "cre:workflow:compute", compute.ResourcePool) + assert.Equal(t, "cre:workflow:compute", compute.ResourcePoolID) +} From eed474e7f23c8e9bf41c8a7abae65830bcdd06d3 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 6 Oct 2026 12:37:03 -0400 Subject: [PATCH 4/5] loop: trim CapabilityUsageEnabled and CapabilityUsageConfig comments Co-Authored-By: Claude Fable 5.1 --- pkg/loop/config.go | 4 +--- pkg/loop/metering_config.go | 7 +------ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 10c4cd20e7..376b1488d2 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -210,9 +210,7 @@ type EnvConfig struct { MeterSnapshotsEnabled bool // CapabilityUsageEnabled enables per-capability workflow usage MeterRecords // (compute, gas) for eventually consistent billing. Independent of - // MeterRecordsEnabled, which gates durable resource metering: producers - // run a dedicated ResourceManager for usage records built from - // CapabilityUsageConfig, so the two flags never share a gate. + // MeterRecordsEnabled, which gates durable resource metering. CapabilityUsageEnabled bool // MeterProduct / MeterTenant / MeterNumericTenantID / MeterEnvironment / diff --git a/pkg/loop/metering_config.go b/pkg/loop/metering_config.go index bb662c8f29..e36d0dc2a0 100644 --- a/pkg/loop/metering_config.go +++ b/pkg/loop/metering_config.go @@ -37,12 +37,7 @@ func (e *EnvConfig) MeteringConfig(emitter resourcemanager.Emitter) resourcemana // CapabilityUsageConfig maps this EnvConfig to the resourcemanager.Config for // a dedicated workflow capability usage ResourceManager (compute, gas -// records). It is gated by CapabilityUsageEnabled alone: MeterRecordsEnabled -// governs durable resource metering and must not switch billing records on or -// off, so the usage manager gets its own ResourceManagerConfig with records -// enabled iff CapabilityUsageEnabled, and snapshots always off (usage records -// have no level to snapshot). Same emitter and deployment identity as -// MeteringConfig. +// records). func (e *EnvConfig) CapabilityUsageConfig(emitter resourcemanager.Emitter) resourcemanager.Config { if e == nil { return resourcemanager.Config{} From 48f88a916c03c1ba091dcf0793b9554378f1536d Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 6 Oct 2026 13:10:42 -0400 Subject: [PATCH 5/5] loop: drop CapabilityUsageEnabled; usage records share MeterRecordsEnabled Per review, capability usage records (compute, gas) do not need their own gate. The existing MeterRecordsEnabled / MeterSnapshotsEnabled knobs already control emission per node and are sufficient for local-cre tuning, so producers reuse EnvConfig.MeteringConfig for the usage ResourceManager. Removes CL_CAPABILITY_USAGE_ENABLED, EnvConfig.CapabilityUsageEnabled and EnvConfig.CapabilityUsageConfig with their tests. Co-Authored-By: Claude Fable 5.1 --- pkg/loop/config.go | 10 ---------- pkg/loop/config_test.go | 3 --- pkg/loop/metering_config.go | 13 ------------- pkg/loop/metering_config_test.go | 19 ------------------- 4 files changed, 45 deletions(-) diff --git a/pkg/loop/config.go b/pkg/loop/config.go index 376b1488d2..cf4d535639 100644 --- a/pkg/loop/config.go +++ b/pkg/loop/config.go @@ -90,7 +90,6 @@ const ( envTelemetryLogCompressor = "CL_TELEMETRY_LOG_COMPRESSOR" envMeterRecordsEnabled = "CL_METER_RECORDS_ENABLED" envMeterSnapshotsEnabled = "CL_METER_SNAPSHOTS_ENABLED" - envCapabilityUsageEnabled = "CL_CAPABILITY_USAGE_ENABLED" envMeterProduct = "CL_METER_PRODUCT" envMeterTenant = "CL_METER_TENANT" envMeterNumericTenantID = "CL_METER_NUMERIC_TENANT_ID" @@ -208,10 +207,6 @@ type EnvConfig struct { TelemetryLogCompressor string MeterRecordsEnabled bool MeterSnapshotsEnabled bool - // CapabilityUsageEnabled enables per-capability workflow usage MeterRecords - // (compute, gas) for eventually consistent billing. Independent of - // MeterRecordsEnabled, which gates durable resource metering. - CapabilityUsageEnabled bool // MeterProduct / MeterTenant / MeterNumericTenantID / MeterEnvironment / // MeterZone / MeterNodeID are @@ -332,7 +327,6 @@ func (e *EnvConfig) AsCmdEnv() (env []string) { add(envTelemetryLogCompressor, e.TelemetryLogCompressor) add(envMeterRecordsEnabled, strconv.FormatBool(e.MeterRecordsEnabled)) add(envMeterSnapshotsEnabled, strconv.FormatBool(e.MeterSnapshotsEnabled)) - add(envCapabilityUsageEnabled, strconv.FormatBool(e.CapabilityUsageEnabled)) add(envMeterProduct, e.MeterProduct) add(envMeterTenant, e.MeterTenant) add(envMeterNumericTenantID, e.MeterNumericTenantID) @@ -655,10 +649,6 @@ func (e *EnvConfig) parse() error { if err != nil { return fmt.Errorf("failed to parse %s: %w", envMeterSnapshotsEnabled, err) } - e.CapabilityUsageEnabled, err = getBool(envCapabilityUsageEnabled) - if err != nil { - return fmt.Errorf("failed to parse %s: %w", envCapabilityUsageEnabled, err) - } e.MeterProduct = os.Getenv(envMeterProduct) e.MeterTenant = os.Getenv(envMeterTenant) diff --git a/pkg/loop/config_test.go b/pkg/loop/config_test.go index 1b96beb1f3..c128f82ec2 100644 --- a/pkg/loop/config_test.go +++ b/pkg/loop/config_test.go @@ -89,7 +89,6 @@ func TestEnvConfig_parse(t *testing.T) { envTelemetryPrometheusBridgePrefixes: "foo,bar", envMeterRecordsEnabled: "true", envMeterSnapshotsEnabled: "false", - envCapabilityUsageEnabled: "true", envMeterProduct: "cre-mainline", envMeterTenant: "mainline", envMeterNumericTenantID: "42", @@ -231,7 +230,6 @@ var envCfgFull = EnvConfig{ TelemetryPrometheusBridgePrefixes: []string{"foo", "bar"}, MeterRecordsEnabled: true, MeterSnapshotsEnabled: false, - CapabilityUsageEnabled: true, MeterProduct: "cre-mainline", MeterTenant: "mainline", MeterNumericTenantID: "42", @@ -315,7 +313,6 @@ func TestEnvConfig_AsCmdEnv(t *testing.T) { assert.Equal(t, "foo,bar", got[envTelemetryPrometheusBridgePrefixes]) assert.Equal(t, "true", got[envMeterRecordsEnabled]) assert.Equal(t, "false", got[envMeterSnapshotsEnabled]) - assert.Equal(t, "true", got[envCapabilityUsageEnabled]) assert.Equal(t, "cre-mainline", got[envMeterProduct]) assert.Equal(t, "mainline", got[envMeterTenant]) assert.Equal(t, "42", got[envMeterNumericTenantID]) diff --git a/pkg/loop/metering_config.go b/pkg/loop/metering_config.go index e36d0dc2a0..2c82535fa0 100644 --- a/pkg/loop/metering_config.go +++ b/pkg/loop/metering_config.go @@ -34,16 +34,3 @@ func (e *EnvConfig) MeteringConfig(emitter resourcemanager.Emitter) resourcemana }, } } - -// CapabilityUsageConfig maps this EnvConfig to the resourcemanager.Config for -// a dedicated workflow capability usage ResourceManager (compute, gas -// records). -func (e *EnvConfig) CapabilityUsageConfig(emitter resourcemanager.Emitter) resourcemanager.Config { - if e == nil { - return resourcemanager.Config{} - } - cfg := e.MeteringConfig(emitter) - cfg.MeterRecordsEnabled = e.CapabilityUsageEnabled - cfg.MeterSnapshotsEnabled = false - return cfg -} diff --git a/pkg/loop/metering_config_test.go b/pkg/loop/metering_config_test.go index 58baa17d72..3478d2b428 100644 --- a/pkg/loop/metering_config_test.go +++ b/pkg/loop/metering_config_test.go @@ -55,22 +55,3 @@ func TestEnvConfig_MeteringConfig(t *testing.T) { assert.Equal(t, "clp-cre-wf-zone-a-1", cfg.DeploymentIdentity.NodeID) }) } - -func TestEnvConfig_CapabilityUsageConfig(t *testing.T) { - t.Run("gated by CapabilityUsageEnabled only", func(t *testing.T) { - e := &EnvConfig{MeterRecordsEnabled: false, MeterSnapshotsEnabled: true, CapabilityUsageEnabled: true, MeterProduct: "cre", MeterNodeID: "n1"} - cfg := e.CapabilityUsageConfig(nil) - assert.True(t, cfg.MeterRecordsEnabled, "usage records on even though durable MeterRecordsEnabled is off") - assert.False(t, cfg.MeterSnapshotsEnabled, "usage records never snapshot") - assert.Equal(t, "cre", cfg.DeploymentIdentity.Product) - assert.Equal(t, "n1", cfg.DeploymentIdentity.NodeID) - }) - t.Run("off when CapabilityUsageEnabled is false regardless of MeterRecordsEnabled", func(t *testing.T) { - e := &EnvConfig{MeterRecordsEnabled: true, CapabilityUsageEnabled: false} - assert.False(t, e.CapabilityUsageConfig(nil).MeterRecordsEnabled) - }) - t.Run("nil receiver", func(t *testing.T) { - var e *EnvConfig - assert.Equal(t, resourcemanager.Config{}, e.CapabilityUsageConfig(nil)) - }) -}