Skip to content

Conversation

@turip
Copy link
Member

@turip turip commented Jan 30, 2026

Overview

A balance check resolves 7 times the customer. It's a heavy operation so let's not resolve it that many times.

This will also shave off 50ms from the calculation time.

This patch decreases that number by at least 5.

Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Entitlement API now returns entitlements alongside their associated customer data.
    • Reset events upgraded to a new version with a slimmer payload.
  • Refactor

    • Entitlement payloads and flows now use explicit customer identifiers and resolve customer details separately, enabling batch customer lookups and consistent ownership handling.
    • Entitlement ownership and scheduling flows updated to carry customer context.
  • Tests

    • Tests added/updated for customer-aware entitlement retrieval and ownership paths.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Validator registration was moved out of NewCustomerService into NewEntitlementRegistry; CustomerService is created earlier and passed into entitlement registry and various entitlement layers. Entitlement model now uses CustomerID instead of embedded Customer; many adapters, services, and wiring updated to be customer-aware.

Changes

Cohort / File(s) Summary
DI & App wiring
app/common/customer.go, app/common/entitlement.go, cmd/balance-worker/wire_gen.go, cmd/billing-worker/wire_gen.go, cmd/jobs/internal/wire_gen.go, cmd/server/wire_gen.go
Removed entitlement-validator wiring from NewCustomerService; NewEntitlementRegistry signature now accepts customer.Service and registers the validator. Application wiring creates CustomerService earlier and passes it into EntitlementRegistry; cleanup/error unwind updated.
Registry & builder
openmeter/registry/builder/entitlement.go, app/common/entitlement.go
Plumbed CustomerService into EntitlementOptions / service config; registry now constructs the entitlement validator and registers it with CustomerService.
Entitlement domain model
openmeter/entitlement/entitlement.go, openmeter/entitlement/uniqueness.go, openmeter/entitlement/uniqueness_test.go, test/notification/consumer_balance.go
Replaced embedded Customer with CustomerID string in GenericProperties; validations, grouping and tests updated to use CustomerID.
Service API & impl
openmeter/entitlement/connector.go, openmeter/entitlement/service/service.go, openmeter/entitlement/service/scheduling.go
Added GetEntitlementWithCustomer / ListEntitlementsWithCustomer and EntitlementWithCustomer/result types. Entitlement service now depends on customer.Service and batch-resolves customers via expandCustomers.
Adapter & DB layer
openmeter/entitlement/adapter/entitlement.go, openmeter/entitlement/adapter/entitlement_test.go
Removed eager WithCustomer edge preloads; queries use CustomerID and mapping/tests no longer expect loaded Customer edge.
Drivers, parsers & API handlers
openmeter/entitlement/driver/parser.go, openmeter/entitlement/driver/entitlement.go, openmeter/entitlement/driver/v2/...
Parsers and handlers now operate on EntitlementWithCustomer; endpoints call customer-aware service methods and validate customer presence when mapping responses.
Events & reset flow
openmeter/entitlement/events.go, openmeter/entitlement/metered/events.go, openmeter/entitlement/metered/reset.go, openmeter/entitlement/balanceworker/*.go
Introduced EntitlementResetEventV3 (replaces V2), removed CustomerUsageAttribution, event payloads now accept explicit customer param; reset and balance handlers updated to use CustomerID and V3 event type.
Grant ownership & adapters
openmeter/entitlement/metered/grant_owner_adapter.go, openmeter/entitlement/metered/*_test.go
Grant owner adapter now depends on customer.Service and fetches the customer via service in DescribeOwner; tests updated to wire customer service.
Subscription integration
openmeter/subscription/entitlement.go, openmeter/subscription/entitlement/adapter.go, openmeter/subscription/*
Subscription entitlements now carry EntitlementWithCustomer; removed some legacy adapter methods and switched to ListEntitlementsWithCustomer flows.
Tests & test infra
test/*, openmeter/*/*_test.go, openmeter/server/server_test.go
Test fixtures, helpers, and no-op connectors updated to initialize and pass customer.Service into entitlement registry, adapters, owner adapters; no-op connector gained customer-aware methods.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Init as Init/Wiring
  participant CustSvc as CustomerService
  participant EntReg as EntitlementRegistry
  participant Validator as EntitlementValidator

  Init->>CustSvc: NewCustomerService(logger, db, publisher)
  activate CustSvc
  CustSvc-->>Init: customerService
  deactivate CustSvc

  Init->>EntReg: NewEntitlementRegistry(..., customerService)
  activate EntReg
  EntReg->>Validator: NewValidator(entRepo)
  activate Validator
  Validator-->>EntReg: validator
  deactivate Validator

  EntReg->>CustSvc: customerService.RegisterRequestValidator(validator)
  EntReg-->>Init: entRegistry
  deactivate EntReg
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • chrisgacsal
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring to avoid redundant customer resolution queries during entitlement retrieval, which directly aligns with the PR's core objective of reducing database calls and improving performance.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/decrease-db-usage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

A balance check resolves 5-10 times the customer. It's a heavy operation
so let's not resolve it that many times.
@turip turip force-pushed the refactor/decrease-db-usage branch from 616fcf2 to 48db2fb Compare January 30, 2026 10:24
@turip turip marked this pull request as ready for review January 30, 2026 12:35
@turip turip requested a review from a team as a code owner January 30, 2026 12:35
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/billing/suite.go (1)

146-158: ⚠️ Potential issue | 🟠 Major

CustomerService is nil when building the entitlement registry.
GetEntitlementRegistry is called before s.CustomerService is initialized (it’s set later), so the registry receives a nil dependency. That can cause panics or missing customer validation in tests. Moving customer setup above the registry creation fixes it.

🛠️ Proposed fix (move customer setup before entitlement registry)
@@
-	// Entitlement
-	entitlementRegistry := registrybuilder.GetEntitlementRegistry(registrybuilder.EntitlementOptions{
-		DatabaseClient:     dbClient,
-		StreamingConnector: s.MockStreamingConnector,
-		Logger:             slog.Default(),
-		MeterService:       s.MeterAdapter,
-		CustomerService:    s.CustomerService,
-		Publisher:          publisher,
-		EntitlementsConfiguration: config.EntitlementsConfiguration{
-			GracePeriod: datetime.ISODurationString("P1D"),
-		},
-		Locker: locker,
-	})
+	// Customer (needs to exist before entitlements)
+	customerAdapter, err := customeradapter.New(customeradapter.Config{
+		Client: dbClient,
+		Logger: slog.Default(),
+	})
+	require.NoError(t, err)
+
+	customerService, err := customerservice.New(customerservice.Config{
+		Adapter:   customerAdapter,
+		Publisher: publisher,
+	})
+	require.NoError(t, err)
+	s.CustomerService = customerService
+
+	// Entitlement
+	entitlementRegistry := registrybuilder.GetEntitlementRegistry(registrybuilder.EntitlementOptions{
+		DatabaseClient:     dbClient,
+		StreamingConnector: s.MockStreamingConnector,
+		Logger:             slog.Default(),
+		MeterService:       s.MeterAdapter,
+		CustomerService:    customerService,
+		Publisher:          publisher,
+		EntitlementsConfiguration: config.EntitlementsConfiguration{
+			GracePeriod: datetime.ISODurationString("P1D"),
+		},
+		Locker: locker,
+	})
@@
-	// Customer
-	customerAdapter, err := customeradapter.New(customeradapter.Config{
-		Client: dbClient,
-		Logger: slog.Default(),
-	})
-	require.NoError(t, err)
-
-	customerService, err := customerservice.New(customerservice.Config{
-		Adapter:   customerAdapter,
-		Publisher: publisher,
-	})
-	require.NoError(t, err)
-	s.CustomerService = customerService
+	// Customer setup moved above so entitlement registry receives a non-nil CustomerService.
openmeter/entitlement/events.go (1)

73-90: ⚠️ Potential issue | 🟠 Major

Guard against nil Customer to avoid panics.
ToDomainEntitlement and EventMetadata dereference Customer directly; if a caller passes nil (or a legacy payload omits it), this will panic. Consider explicit validation and/or a safe fallback.

🛡️ One possible guard
 import (
+    "errors"
     "time"
@@
 func (e entitlementEventV2EntitlementLiteral) Validate() error {
+    if e.Customer == nil {
+        return errors.New("customer is required")
+    }
     domainEnt := e.ToDomainEntitlement()
@@
 func (e EntitlementCreatedEventV2) EventMetadata() metadata.EventMetadata {
+    customerID := ""
+    if e.Entitlement.Customer != nil {
+        customerID = e.Entitlement.Customer.ID
+    }
     return metadata.EventMetadata{
         Source:  metadata.ComposeResourcePath(e.Namespace.ID, metadata.EntityEntitlement, e.Entitlement.ID),
-        Subject: metadata.ComposeResourcePath(e.Namespace.ID, metadata.EntityCustomer, e.Entitlement.Customer.ID),
+        Subject: metadata.ComposeResourcePath(e.Namespace.ID, metadata.EntityCustomer, customerID),
     }
 }
@@
 func (e EntitlementDeletedEventV2) EventMetadata() metadata.EventMetadata {
+    customerID := ""
+    if e.Entitlement.Customer != nil {
+        customerID = e.Entitlement.Customer.ID
+    }
     return metadata.EventMetadata{
         Source:  metadata.ComposeResourcePath(e.Namespace.ID, metadata.EntityEntitlement, e.Entitlement.ID),
-        Subject: metadata.ComposeResourcePath(e.Namespace.ID, metadata.EntityCustomer, e.Entitlement.Customer.ID),
+        Subject: metadata.ComposeResourcePath(e.Namespace.ID, metadata.EntityCustomer, customerID),
     }
 }

Also applies to: 166-170, 197-200

🤖 Fix all issues with AI agents
In `@openmeter/entitlement/driver/v2/entitlement.go`:
- Around line 112-113: The code dereferences r returned from
ParserV2.ToAPIGenericV2 without checking err, which can panic if the function
returns (nil, error); change the call to capture r, err :=
ParserV2.ToAPIGenericV2(&e, cust.ID, cust.Key), then check if err != nil and
immediately return the zero value for the expected return type and err (e.g.,
return APIGenericV2{}, err), otherwise safely dereference r and return *r, nil;
apply this change in the function where ParserV2.ToAPIGenericV2 is invoked to
avoid nil pointer dereference.

In `@openmeter/entitlement/metered/grant_owner_adapter.go`:
- Around line 99-112: The customer deletion check in GrantOwnerAdapter (around
the GetCustomer call) should use the cust.IsDeleted() helper rather than
checking DeletedAt.Before(clock.Now()), so replace the conditional "cust == nil
|| (cust.DeletedAt != nil && cust.DeletedAt.Before(clock.Now()))" with "cust ==
nil || cust.IsDeleted()" in the handler that validates the customer after
customerService.GetCustomer; this aligns behavior with other handlers and
correctly handles the equal-to-now case.

In `@openmeter/subscription/testutils/service.go`:
- Around line 100-103: The test constructs separate instances causing hooks to
attach to a different subject.Service than the one used inside
NewCustomerService; instead instantiate a single Subject service and share it:
create subjectService via NewSubjectService first, then create customerAdapter
with that same subjectService (NewCustomerAdapter(..., subjectService)) and pass
the same subjectService into NewCustomerService (or change NewCustomerService to
accept a subjectService/customerAdapter parameter) so customerAdapter,
customerService and subjectService all reference the same subject.Service
instance and hooks register on the correct object.
🧹 Nitpick comments (8)
openmeter/entitlement/metered/events.go (1)

81-88: Variable name inconsistency: resetEntitlementEventNameV2 should be resetEntitlementEventNameV3.

The variable is named V2 but it's actually holding the v3 event name (Version: "v3"). This is confusing for future maintainers.

Also, the interface check on line 82 verifies EntitlementResetEvent (v1) implements marshaler.Event, but there's no check for EntitlementResetEventV3.

♻️ Suggested fix
 var (
-	_ marshaler.Event = EntitlementResetEvent{}
+	_ marshaler.Event = EntitlementResetEvent{}
+	_ marshaler.Event = EntitlementResetEventV3{}

-	resetEntitlementEventNameV2 = metadata.GetEventName(metadata.EventType{
+	resetEntitlementEventNameV3 = metadata.GetEventName(metadata.EventType{
 		Subsystem: EventSubsystem,
 		Name:      "entitlement.reset",
 		Version:   "v3",
 	})
 )

And update the reference in EventName():

 func (e EntitlementResetEventV3) EventName() string {
-	return resetEntitlementEventNameV2
+	return resetEntitlementEventNameV3
 }
openmeter/entitlement/metered/reset.go (1)

56-65: Consider using clock.Now() instead of time.Now() for testability.

Line 64 uses time.Now() for ResetRequestedAt, but elsewhere in the codebase (and in tests), clock.Now() is used to enable time manipulation in tests. This could make it harder to write deterministic tests for reset event timestamps.

♻️ Suggested fix
 		CustomerID:       ent.CustomerID,
 		ResetAt:          params.At,
 		RetainAnchor:     params.RetainAnchor,
-		ResetRequestedAt: time.Now(),
+		ResetRequestedAt: clock.Now(),
 	}
openmeter/entitlement/driver/entitlement.go (1)

276-286: Consider reordering nil check before IsDeleted check.

The current code checks cust.IsDeleted() at line 276 before checking if cust == nil at line 282. If cust is nil, calling cust.IsDeleted() would panic. However, looking more carefully, the nil check for the cust != nil condition is part of the compound condition on line 276, so this is actually safe.

Actually, wait - line 276 has if cust != nil && cust.IsDeleted(), so the nil case falls through to lines 282-286. This is correct but a bit scattered. Consider consolidating:

♻️ Suggested consolidation for clarity
+			if cust == nil {
+				return GetEntitlementsOfSubjectHandlerResponse{}, models.NewGenericPreConditionFailedError(
+					fmt.Errorf("customer not found [namespace=%s subject.id=%s]", id.Namespace, id.SubjectIdOrKey),
+				)
+			}
+
-			if cust != nil && cust.IsDeleted() {
+			if cust.IsDeleted() {
 				return GetEntitlementsOfSubjectHandlerResponse{}, models.NewGenericPreConditionFailedError(
 					fmt.Errorf("customer is deleted [namespace=%s customer.id=%s]", cust.Namespace, cust.ID),
 				)
 			}
-
-			if cust == nil {
-				return GetEntitlementsOfSubjectHandlerResponse{}, models.NewGenericPreConditionFailedError(
-					fmt.Errorf("customer not found [namespace=%s subject.id=%s]", id.Namespace, id.SubjectIdOrKey),
-				)
-			}
openmeter/entitlement/uniqueness.go (1)

37-41: Consider updating the error message for clarity.

The grouping is now by CustomerID, but the error message still says "entitlements must belong to the same subject". Since "subject" and "customer" appear to be distinct concepts in this codebase, it might be worth updating the message to say "entitlements must belong to the same customer" to avoid confusion during debugging.

💡 Suggested change
 	if grouped := lo.GroupBy(ents, func(e Entitlement) string { return e.CustomerID }); len(grouped) > 1 {
 		keys := lo.Keys(grouped)
 		slices.Sort(keys)
-		return fmt.Errorf("entitlements must belong to the same subject, found %v", keys)
+		return fmt.Errorf("entitlements must belong to the same customer, found %v", keys)
 	}
openmeter/entitlement/balanceworker/worker.go (1)

287-298: Comment mentions V2 but handler processes V3.

The comment on line 287 says "Metered entitlement reset event v2" but the handler now processes EntitlementResetEventV3. Consider updating the comment to match.

📝 Suggested comment update
-		// Metered entitlement reset event v2
+		// Metered entitlement reset event v3
 		grouphandler.NewGroupEventHandler(func(ctx context.Context, event *meteredentitlement.EntitlementResetEventV3) error {
openmeter/entitlement/service/scheduling.go (1)

28-130: Consider deferring the customer lookup until after feature/uniqueness checks.
This avoids a DB hit when validation fails early (e.g., missing feature or uniqueness conflict).

♻️ Possible refactor
-        customer, err := c.customerService.GetCustomer(ctx, customer.GetCustomerInput{
-            CustomerID: &customer.CustomerID{
-                Namespace: input.Namespace,
-                ID:        input.UsageAttribution.ID,
-            },
-        })
-        if err != nil {
-            return nil, err
-        }
+        // ... after feature lookup + uniqueness validation ...
+        customer, err := c.customerService.GetCustomer(ctx, customer.GetCustomerInput{
+            CustomerID: &customer.CustomerID{
+                Namespace: input.Namespace,
+                ID:        input.UsageAttribution.ID,
+            },
+        })
+        if err != nil {
+            return nil, err
+        }

As per coding guidelines: Performance should be a priority in critical code paths. Anything related to event ingestion, message processing, database operations (regardless of database) should be vetted for potential performance bottlenecks.

openmeter/entitlement/driver/parser.go (2)

35-41: Consider extracting the repeated subject key resolution logic.

The same pattern for extracting subjKey from e.Customer.UsageAttribution appears three times (in ToMetered, ToStatic, and ToBoolean). Extracting this to a small helper would reduce duplication and make future changes easier.

♻️ Suggested helper extraction
// Add this helper function somewhere in the file
func getSubjectKey(c customer.Customer) string {
	if c.UsageAttribution == nil {
		return ""
	}
	key, err := c.UsageAttribution.GetFirstSubjectKey()
	if err != nil {
		return ""
	}
	return key
}

Then each method simply calls:

subjKey := getSubjectKey(e.Customer)

Also applies to: 80-86, 119-125


52-52: Placeholder comment for IsUnlimited field.

There's a // implement comment here indicating this is hardcoded to false. If unlimited entitlements are planned, it'd be good to track this as a follow-up task so it doesn't get lost.

Would you like me to open an issue to track implementing the IsUnlimited logic?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openmeter/entitlement/driver/entitlement.go (1)

103-115: ⚠️ Potential issue | 🟠 Major

Guard against nil customer before dereferencing.

If resolveCustomerFromSubject returns (nil, nil), both cust.GetUsageAttribution() and *cust will panic. Please add a nil check and return a clear precondition error in both Create and Override flows.

🔧 Suggested fix
 func (h *entitlementHandler) CreateEntitlement() CreateEntitlementHandler {
   return httptransport.NewHandlerWithArgs[CreateEntitlementHandlerRequest, CreateEntitlementHandlerResponse, string](
@@
     func(ctx context.Context, request CreateEntitlementHandlerRequest) (CreateEntitlementHandlerResponse, error) {
       cust, err := h.resolveCustomerFromSubject(ctx, request.Namespace, request.SubjectIdOrKey)
       if err != nil {
         return nil, err
       }
+      if cust == nil {
+        return nil, models.NewGenericPreConditionFailedError(
+          fmt.Errorf("customer not found [namespace=%s subject.id=%s]", request.Namespace, request.SubjectIdOrKey),
+        )
+      }
 
       request.Inputs.UsageAttribution = cust.GetUsageAttribution()
@@
 func (h *entitlementHandler) OverrideEntitlement() OverrideEntitlementHandler {
   return httptransport.NewHandlerWithArgs[OverrideEntitlementHandlerRequest, OverrideEntitlementHandlerResponse, OverrideEntitlementHandlerParams](
@@
     func(ctx context.Context, request OverrideEntitlementHandlerRequest) (OverrideEntitlementHandlerResponse, error) {
       cust, err := h.resolveCustomerFromSubject(ctx, request.Inputs.Namespace, request.SubjectIdOrKey)
       if err != nil {
         return nil, err
       }
+      if cust == nil {
+        return nil, models.NewGenericPreConditionFailedError(
+          fmt.Errorf("customer not found [namespace=%s subject.id=%s]", request.Inputs.Namespace, request.SubjectIdOrKey),
+        )
+      }
 
       request.Inputs.UsageAttribution = cust.GetUsageAttribution()

Also applies to: 167-179

🧹 Nitpick comments (3)
openmeter/entitlement/driver/v2/entitlement.go (1)

151-156: Variable shadows the imported package name.

The local variable entitlement at line 151 shadows the imported entitlement package (line 13). It works here since the package isn't used after this point in the scope, but it can trip up readers or future edits. Consider renaming to something like ent or result for clarity.

♻️ Suggested rename
-			entitlement, err := h.connector.GetEntitlementWithCustomer(ctx, request.Namespace, request.EntitlementId)
+			ent, err := h.connector.GetEntitlementWithCustomer(ctx, request.Namespace, request.EntitlementId)
 			if err != nil {
 				return nil, err
 			}

-			return ParserV2.ToAPIGenericV2(&entitlement.Entitlement, entitlement.Customer.ID, entitlement.Customer.Key)
+			return ParserV2.ToAPIGenericV2(&ent.Entitlement, ent.Customer.ID, ent.Customer.Key)
openmeter/entitlement/metered/reset.go (1)

57-65: Guard against missing CustomerID before publishing.

If any legacy entitlements lack CustomerID, the v3 event validation will fail after the reset work, and the call returns an error. Can you confirm the invariant (or backfill)? If not guaranteed, consider an early guard for clearer failure.

🔧 Possible early validation
+		if ent.CustomerID == "" {
+			return nil, errors.New("customerID must be set for metered entitlement reset")
+		}
+
 		event := EntitlementResetEventV3{
 			EntitlementID: entitlementID.ID,
 			Namespace: eventmodels.NamespaceID{
 				ID: entitlementID.Namespace,
 			},
 			CustomerID:       ent.CustomerID,
 			ResetAt:          params.At,
 			RetainAnchor:     params.RetainAnchor,
 			ResetRequestedAt: clock.Now(),
 		}
openmeter/entitlement/metered/events.go (1)

81-88: Add a compile-time interface assertion for the v3 event.

Small maintainability win: you already assert EntitlementResetEvent implements marshaler.Event; adding the same for EntitlementResetEventV3 makes future refactors safer.

♻️ Suggested tweak
 var (
 	_ marshaler.Event = EntitlementResetEvent{}
+	_ marshaler.Event = EntitlementResetEventV3{}
 
 	resetEntitlementEventNameV3 = metadata.GetEventName(metadata.EventType{
 		Subsystem: EventSubsystem,
 		Name:      "entitlement.reset",
 		Version:   "v3",
 	})
 )

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/billing/suite.go`:
- Around line 169-181: The EntitlementOptions passed into
registrybuilder.GetEntitlementRegistry is missing a Tracer; add Tracer:
noop.NewTracerProvider().Tracer("test_env") to the EntitlementOptions literal so
NewEntitlementGrantOwnerAdapter, creditConnector, and
NewMeteredEntitlementConnector receive a non-nil tracer; locate the
EntitlementOptions block used in registrybuilder.GetEntitlementRegistry and add
the Tracer field consistent with other test setup code that uses
noop.NewTracerProvider().Tracer("test_env").

@turip turip requested a review from GAlexIHU January 30, 2026 14:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openmeter/entitlement/metered/events.go (1)

17-17: ⚠️ Potential issue | 🟡 Minor

Fix the deprecation comment to point to V3 instead of the nonexistent V2.

The deprecation comment at line 17 says to use EntitlementResetEventV2, but that type was completely removed—V2 doesn't exist anywhere in the codebase. V3 is what's actually being used now. The comment should point to EntitlementResetEventV3 instead to avoid sending developers down a confusing dead-end during migrations.

Copy link
Contributor

@GAlexIHU GAlexIHU left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get why this change is wanted and i'm ok with it being merged but I don't really like the way it's done.

Some thoughts on possible approaches:

  1. If we're removing customer from entitlement then we could just use the customer service separately every place customer info is needed
    e.g. roughlt
ents, err := s.entitlementService.ListEntitlements(...)

custs, err := s.customerService.ListCustomers(mapEntsToCustIds(ents))

out, err := matchCustomersToEnts(ents, custs)
  1. If we're not removing it then we already have the expand pattern and optional types in the codebase, we could populate the field conditionally.

  2. If this is primarily a concern for value checks IMO we should just rewrite that flow so it pre-fetches everything approximately once (though I recognize that's significantly more effort than this)

Also, though I recognize that from OMC's perspective this could save some costs, will we actually materialize that?

These are just thoughts / conversation topics, I'm fine with the change as is (except that one comment), I don't expect any of this to change

}

// Create and register the entitlement validator
validator, err := entitlementvalidator.NewValidator(entRegistry.EntitlementRepo)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure if there's a service where customer svc is used but not entitlement, but if there is, customer svc should still have the entitlement validator, which it won't after this change

@turip turip merged commit e779845 into main Jan 30, 2026
29 checks passed
@turip turip deleted the refactor/decrease-db-usage branch January 30, 2026 15:56
@turip
Copy link
Member Author

turip commented Jan 30, 2026

I get why this change is wanted and i'm ok with it being merged but I don't really like the way it's done.

Some thoughts on possible approaches:

  1. If we're removing customer from entitlement then we could just use the customer service separately every place customer info is needed
    e.g. roughlt
ents, err := s.entitlementService.ListEntitlements(...)

custs, err := s.customerService.ListCustomers(mapEntsToCustIds(ents))

out, err := matchCustomersToEnts(ents, custs)
  1. If we're not removing it then we already have the expand pattern and optional types in the codebase, we could populate the field conditionally.
  2. If this is primarily a concern for value checks IMO we should just rewrite that flow so it pre-fetches everything approximately once (though I recognize that's significantly more effort than this)

Also, though I recognize that from OMC's perspective this could save some costs, will we actually materialize that?

These are just thoughts / conversation topics, I'm fine with the change as is (except that one comment), I don't expect any of this to change

Just from propsperity's sake:
The option 3 should be the good solution, but entitlements is to be refactored, so for now it doesn't worth the effort to pursue it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants