Skip to content

Conversation

@GAlexIHU
Copy link
Contributor

@GAlexIHU GAlexIHU commented Dec 12, 2025

Overview

We obviously need to be able to group by subject

Summary by CodeRabbit

  • Refactor
    • Simplified meter export configuration by removing the export subject requirement.
    • Restricted group-by functionality to subject-based grouping only.
    • Updated export data documentation regarding customer handling.

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

@GAlexIHU GAlexIHU requested a review from a team as a code owner December 12, 2025 14:28
@GAlexIHU GAlexIHU added the release-note/bug-fix Release note: Bug Fixes label Dec 12, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

📝 Walkthrough

Walkthrough

This PR refactors the meterexport service to eliminate the required ExportSubject configuration field and instead derive subject information directly from meter row data. Validation rules are updated to allow subject-based grouping in funnel queries while subjects in exported events now use actual row data instead of a static config value.

Changes

Cohort / File(s) Change Summary
Service Configuration
openmeter/meterexport/service/service.go
Removed the ExportSubject field from the Config struct and its corresponding validation check, eliminating the requirement for a static export subject in service configuration.
Validation & Data Transformation
openmeter/meterexport/service/funnel.go, openmeter/meterexport/service/syntheticdata.go
Removed FilterSubject validation, updated GroupBy validation to allow only "subject" value (previously disallowed all GroupBy), and changed event subject derivation to use lo.FromPtr(row.Subject) from meter rows instead of the config value. Added GroupBy: ["subject"] to funnel query parameters.
Test Updates
openmeter/meterexport/service/service_test.go
Removed ExportSubject field initialization from test Config constructions and eliminated test cases validating missing export subject scenarios.
Documentation
openmeter/meterexport/service.go
Updated ExportSyntheticMeterData comment to remove mention of Subjects being excluded from exported data.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Cohesiveness: The changes form a logical refactoring that removes a config requirement across multiple touchpoints, making it easier to reason about as a whole.
  • Areas requiring extra attention:
    • Verify that removing ExportSubject doesn't break downstream consumers relying on this config field
    • Confirm the GroupBy validation change from disallow-all to allow-only-subject is intentional and correctly enforced
    • Double-check that using lo.FromPtr(row.Subject) handles nil cases appropriately in all scenarios

Possibly related PRs

  • PR #3674: The original PR that introduced the Config.ExportSubject field that this PR removes, making this a follow-up refactoring to that functionality.

Suggested labels

area/processor, release-note/feature

Suggested reviewers

  • chrisgacsal
  • tothandras

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(meterexport): groupby subject' directly reflects the main changes: enabling GroupBy with 'subject' value support and removing ExportSubject dependency, which aligns with the PR's primary objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 fix/meterexport

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.

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

Caution

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

⚠️ Outside diff range comments (3)
openmeter/meterexport/service.go (1)

23-33: Doc note looks stale now that GroupBy: ["subject"] is supported (at least internally).
Line 30 still says “GroupBy values are not yet supported”, but this PR adds subject grouping—worth updating to avoid confusing API consumers.

openmeter/meterexport/service/syntheticdata.go (2)

58-118: Bug: consumer goroutine can spin forever once meterRowErrCh is closed.
When meterRowErrCh is closed, case err, ok := <-meterRowErrCh will keep firing with ok=false, and the loop never exits / never hits the meterRowCh closed path.

 g.Go(func() error {
+    // Avoid select spinning on closed channels
+    meterRowErrChLocal := meterRowErrCh
+    meterRowChLocal := meterRowCh
 	for {
 		select {
 		case <-ctx.Done():
 			sendCtxErr()
 			return nil
-		case err, ok := <-meterRowErrCh:
+		case err, ok := <-meterRowErrChLocal:
+			if !ok {
+				meterRowErrChLocal = nil
+				continue
+			}
 			// Filter out context errors as they're handled via sendCtxErr
-			if ok && err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {
+			if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {
 				errCh <- err
 			}
-		case row, ok := <-meterRowCh:
+		case row, ok := <-meterRowChLocal:
 			if !ok {
 				// Before returning, check if context was canceled
 				// This ensures we always report context cancellation to the caller
 				sendCtxErr()
 				return nil
 			}

120-132: Validate that row.Subject is non-empty before creating events.

Since OpenMeter's business logic requires CloudEvents subject to be present, using lo.FromPtr(row.Subject) can silently produce an empty string if the pointer is nil. This will likely cause ingestion to reject the synthetic events. Add an explicit check at the start of createEventFromMeterRow to fail fast:

 func (s *service) createEventFromMeterRow(m meter.Meter, row meter.MeterQueryRow) (streaming.RawEvent, error) {
+    if row.Subject == nil || *row.Subject == "" {
+        return streaming.RawEvent{}, fmt.Errorf("missing subject in meter row")
+    }
 	// For SUM and COUNT type source meters, all event rows can be represented as SUM meter events
 	baseEvent := streaming.RawEvent{
 		Namespace:  m.Namespace,
 		ID:         ulid.Make().String(),
 		Type:       m.EventType, // We reuse the same type as the source meter
 		Source:     fmt.Sprintf("%s:%s/%s", s.EventSourceGroup, m.Namespace, m.ID),
-		Subject:    lo.FromPtr(row.Subject),
+		Subject:    *row.Subject,
 		IngestedAt: clock.Now(),
 		Time:       row.WindowStart,
 		CustomerID: nil,
 	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35a558c and d8f9288.

📒 Files selected for processing (5)
  • openmeter/meterexport/service.go (1 hunks)
  • openmeter/meterexport/service/funnel.go (1 hunks)
  • openmeter/meterexport/service/service.go (0 hunks)
  • openmeter/meterexport/service/service_test.go (0 hunks)
  • openmeter/meterexport/service/syntheticdata.go (2 hunks)
💤 Files with no reviewable changes (2)
  • openmeter/meterexport/service/service.go
  • openmeter/meterexport/service/service_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them.

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.

Files:

  • openmeter/meterexport/service/funnel.go
  • openmeter/meterexport/service.go
  • openmeter/meterexport/service/syntheticdata.go
🧠 Learnings (2)
📓 Common learnings
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 3486
File: openmeter/ingest/kafkaingest/serializer/serializer.go:105-107
Timestamp: 2025-10-09T13:59:12.012Z
Learning: In OpenMeter, the CloudEvents `subject` field is mandatory for the application's business logic, even though it's optional in the CloudEvents specification. The `ValidateKafkaPayloadToCloudEvent` function in `openmeter/ingest/kafkaingest/serializer/serializer.go` intentionally enforces this requirement.
📚 Learning: 2025-10-09T13:59:12.012Z
Learnt from: chrisgacsal
Repo: openmeterio/openmeter PR: 3486
File: openmeter/ingest/kafkaingest/serializer/serializer.go:105-107
Timestamp: 2025-10-09T13:59:12.012Z
Learning: In OpenMeter, the CloudEvents `subject` field is mandatory for the application's business logic, even though it's optional in the CloudEvents specification. The `ValidateKafkaPayloadToCloudEvent` function in `openmeter/ingest/kafkaingest/serializer/serializer.go` intentionally enforces this requirement.

Applied to files:

  • openmeter/meterexport/service/funnel.go
  • openmeter/meterexport/service/syntheticdata.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Code Generators
  • GitHub Check: Lint
  • GitHub Check: Migration Checks
  • GitHub Check: Build
  • GitHub Check: Test
  • GitHub Check: Repository Scan
  • GitHub Check: Analyze (go)

Comment on lines +56 to 62
// GroupBy subject is allowed (used internally for per-subject export)
for _, g := range p.queryParams.GroupBy {
if g != "subject" {
errs = append(errs, errors.New("group by is only supported for subject"))
break
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

GroupBy is validated but never sent to QueryMeter (so grouping likely does nothing).
Nice validation tweak, but the actual query drops params.queryParams.GroupBy.

 		queryParams := streaming.QueryParams{
 			From:           &queryFrom,
 			To:             &queryTo,
 			WindowSize:     params.queryParams.WindowSize,
 			WindowTimeZone: params.queryParams.WindowTimeZone,
+			GroupBy:        params.queryParams.GroupBy,
 		}

Also applies to: 99-107

🤖 Prompt for AI Agents
In openmeter/meterexport/service/funnel.go around lines 56-62 and again around
99-107, GroupBy is validated but never forwarded to QueryMeter, so grouping has
no effect; update the code paths that build the query params passed to
QueryMeter to include params.queryParams.GroupBy (or the equivalent GroupBy
field) when calling QueryMeter, ensuring the validated GroupBy slice is copied
or referenced into the request object/params sent to QueryMeter in both
locations so grouping is applied at query time.

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

Labels

release-note/bug-fix Release note: Bug Fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants