Skip to content

feat: detailed line persisted flag#4255

Merged
turip merged 4 commits intomainfrom
refactor/detailed-line-persisted-flag
Apr 29, 2026
Merged

feat: detailed line persisted flag#4255
turip merged 4 commits intomainfrom
refactor/detailed-line-persisted-flag

Conversation

@turip
Copy link
Copy Markdown
Member

@turip turip commented Apr 29, 2026

Overview

Safety measure: only mark detailed lines as expanded when the persisted
run records that detailed lines were written at least once. Treating
unknown detailed lines as an empty set can make
late-event rating overcharge.

Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Added tracking of detailed billing line presence for usage-based charges; new runs explicitly start with "no detailed lines".
  • Bug Fixes

    • Fetching and upserting now honor the persisted presence flag and clear stale detailed lines when run metadata is missing.
    • Upserts now mark presence when detailed lines are persisted.
  • Tests

    • Expanded coverage validating presence-flag behavior across create, upsert, fetch, and missing-run scenarios.
  • Chores

    • Database migration to add and backfill the presence flag for existing runs.

@turip turip requested a review from a team as a code owner April 29, 2026 14:44
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

A new boolean column detailed_lines_present was added to ChargeUsageBasedRuns. Creation, fetch, and upsert flows were updated so the run-level flag is initialized on creation, set when detailed lines are upserted, and consulted during fetches to determine whether detailed lines are presented.

Changes

Cohort / File(s) Summary
Schema & Persistence
openmeter/ent/schema/chargesusagebased.go
Added detailed_lines_present boolean field to ChargeUsageBasedRuns.
Database Migrations
tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.up.sql, tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.down.sql
Up migration adds non-null detailed_lines_present (backfills true when detailed-line rows exist or charge status is final) and removes default; down migration drops the column.
Adapter: Detailed Lines
openmeter/billing/charges/usagebased/adapter/detailedline.go
FetchDetailedLines now loads per-run ChargeUsageBasedRuns rows and uses DetailedLinesPresent to decide whether to populate DetailedLines (explicitly sets None() when no run row or flag=false). UpsertRunDetailedLines sets DetailedLinesPresent=true on the run record after deleting existing lines and before committing inserts.
Adapter: Realization Run
openmeter/billing/charges/usagebased/adapter/realizationrun.go
CreateRealizationRun initializes detailed_lines_present=false for new runs.
Tests
openmeter/billing/charges/usagebased/adapter/detailedline_test.go
New tests validate that detailed-line presence is governed by the persisted DetailedLinesPresent flag (initial false, toggles to true after upsert, adapter does not “repair” false flag even if rows exist, stale in-memory lines are overridden), plus a helper createChargeWithRun.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Adapter
    participant DB

    Client->>Adapter: UpsertRunDetailedLines(charge, run, lines)
    Adapter->>DB: BEGIN txn
    Adapter->>DB: DELETE FROM charge_usage_based_run_detailed_line WHERE ...
    Adapter->>DB: INSERT INTO charge_usage_based_run_detailed_line (...) VALUES (...)
    Adapter->>DB: UPDATE charge_usage_based_runs SET detailed_lines_present = true WHERE ...
    DB-->>Adapter: COMMIT
    Adapter-->>Client: success
Loading
sequenceDiagram
    participant Client
    participant Adapter
    participant DB

    Client->>Adapter: FetchDetailedLines(realizations[])
    Adapter->>DB: SELECT detailed_lines_present FROM charge_usage_based_runs WHERE run_id IN (...)
    DB-->>Adapter: rows with detailed_lines_present flags
    Adapter->>DB: (if detailed_lines_present=true) SELECT detailed-line rows for those runs
    DB-->>Adapter: detailed-line rows (only for flagged runs)
    Adapter-->>Client: realizations populated with DetailedLines Some(lines) or None() per flag
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • tothandras
  • GAlexIHU
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: detailed line persisted flag' directly and specifically describes the main change—adding a persisted flag to track whether detailed lines were ever written for a usage-based run.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/detailed-line-persisted-flag

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openmeter/billing/charges/usagebased/adapter/detailedline.go`:
- Around line 76-79: In FetchDetailedLines, when a run's ID isn't found in
detailedLinesPresentByRunID (the current branch with "if !found { continue }"),
explicitly clear any stale in-memory detailed lines for that realization (e.g.,
set charge.Realizations[idx].DetailedLines to nil or an empty slice) before
continuing; update the code that iterates runs/realizations (reference:
detailedLinesPresentByRunID, run.ID.ID, charge.Realizations[idx].DetailedLines,
FetchDetailedLines) so missing persisted run metadata cannot leave a stale
Some(...) DetailedLines behind.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5903d44b-e910-4891-9f01-303ebc23a951

📥 Commits

Reviewing files that changed from the base of the PR and between 3405351 and 44317de.

⛔ Files ignored due to path filters (9)
  • openmeter/ent/db/chargeusagebasedruns.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns/chargeusagebasedruns.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (6)
  • openmeter/billing/charges/usagebased/adapter/detailedline.go
  • openmeter/billing/charges/usagebased/adapter/detailedline_test.go
  • openmeter/billing/charges/usagebased/adapter/realizationrun.go
  • openmeter/ent/schema/chargesusagebased.go
  • tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.down.sql
  • tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.up.sql

Comment thread openmeter/billing/charges/usagebased/adapter/detailedline.go
@turip turip added release-note/misc Miscellaneous changes area/billing labels Apr 29, 2026
Base automatically changed from refactor/detailed-line-convert-ownership to main April 29, 2026 15:18
Copy link
Copy Markdown
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openmeter/billing/charges/usagebased/adapter/detailedline.go`:
- Around line 153-162: Replace the generic Update().Where(...).Save() with an
ID-targeted update to ensure the row is actually modified: call
tx.db.ChargeUsageBasedRuns.UpdateOneID(runID.ID).SetDetailedLinesPresent(true).Save(ctx)
and handle the returned error explicitly (return err), and if you want clearer
failure semantics convert ent NotFound errors into a descriptive error (e.g.,
"ChargeUsageBasedRuns not found for runID") so a missing row doesn’t silently
appear successful; keep the Namespace and ChargeID checks by validating the
loaded entity after Save (compare returned entity's Namespace/ChargeID to
runID.Namespace/chargeID.ID) and return an error if they don’t match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 22ee3087-696a-4656-979d-5a2e8febedab

📥 Commits

Reviewing files that changed from the base of the PR and between 44317de and e2c9f28.

📒 Files selected for processing (2)
  • openmeter/billing/charges/usagebased/adapter/detailedline.go
  • openmeter/billing/charges/usagebased/adapter/detailedline_test.go

Comment thread openmeter/billing/charges/usagebased/adapter/detailedline.go
turip added 4 commits April 29, 2026 17:20
Rating now owns detailed line conversion so that calculations can
use the usagebased detailed line format.

This patch also unifies detailed line fetching by making rating expand
the detailed lines as needed.

This removes the redundant codepath from statemachine/run.
This patch adds a safety feature to the usagebased runs, that makes sure
that we properly report if detailed lines are persisted for a run or not.

This ensures that we are not in an inconsistent state for prior runs.
@turip turip force-pushed the refactor/detailed-line-persisted-flag branch from e2c9f28 to 88dd7ad Compare April 29, 2026 15:20
Copy link
Copy Markdown
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.

♻️ Duplicate comments (1)
openmeter/billing/charges/usagebased/adapter/detailedline.go (1)

153-162: ⚠️ Potential issue | 🟠 Major

Don’t let the run-flag update silently no-op.

Line 153 currently uses Update().Where(...).Save(ctx) and ignores the affected-row count. If that predicate matches zero rows, the method still returns success, DetailedLinesPresent stays false, and a later FetchDetailedLines will hide persisted lines behind mo.None(). This PR’s safety contract really wants that update to fail hard instead.

Suggested fix
-		if _, err := tx.db.ChargeUsageBasedRuns.Update().
-			Where(
-				dbchargeusagebasedruns.NamespaceEQ(runID.Namespace),
-				dbchargeusagebasedruns.ChargeIDEQ(chargeID.ID),
-				dbchargeusagebasedruns.ID(runID.ID),
-			).
-			SetDetailedLinesPresent(true).
-			Save(ctx); err != nil {
+		if _, err := tx.db.ChargeUsageBasedRuns.UpdateOneID(runID.ID).
+			Where(
+				dbchargeusagebasedruns.NamespaceEQ(runID.Namespace),
+				dbchargeusagebasedruns.ChargeIDEQ(chargeID.ID),
+			).
+			SetDetailedLinesPresent(true).
+			Save(ctx); err != nil {
 			return err
 		}
#!/bin/bash
set -euo pipefail

# Verify the generated Ent builders expose the expected Save/Where semantics.
rg -n -C2 'type ChargeUsageBasedRunsUpdate struct|func \(.*ChargeUsageBasedRunsUpdate\) Save\(|type ChargeUsageBasedRunsUpdateOne struct|func \(.*ChargeUsageBasedRunsUpdateOne\) Save\(|func \(.*ChargeUsageBasedRunsUpdateOne\) Where\(' openmeter/ent/db

Expected result: ChargeUsageBasedRunsUpdate.Save(...) shows the bulk-update path, while ChargeUsageBasedRunsUpdateOne.Save(...) and Where(...) exist on the single-row builder so a missing row becomes an error instead of a silent success.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openmeter/billing/charges/usagebased/adapter/detailedline.go` around lines
153 - 162, The current bulk update using
ChargeUsageBasedRuns.Update().Where(...).SetDetailedLinesPresent(true).Save(ctx)
can silently no-op if zero rows match; change to the single-row builder so a
missing run errors: use ChargeUsageBasedRuns.UpdateOneID(runID.ID) (and still
include NamespaceEQ/ChargeIDEQ checks via Where if desired) or use
ChargeUsageBasedRuns.UpdateOne(...) so that calling Save(ctx) will return an
error when the target row doesn't exist; update the call site in the transaction
code to use the UpdateOne* builder instead of Update() and handle the returned
error as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@openmeter/billing/charges/usagebased/adapter/detailedline.go`:
- Around line 153-162: The current bulk update using
ChargeUsageBasedRuns.Update().Where(...).SetDetailedLinesPresent(true).Save(ctx)
can silently no-op if zero rows match; change to the single-row builder so a
missing run errors: use ChargeUsageBasedRuns.UpdateOneID(runID.ID) (and still
include NamespaceEQ/ChargeIDEQ checks via Where if desired) or use
ChargeUsageBasedRuns.UpdateOne(...) so that calling Save(ctx) will return an
error when the target row doesn't exist; update the call site in the transaction
code to use the UpdateOne* builder instead of Update() and handle the returned
error as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 75ec9e6e-4e35-4765-a10a-eabbb0e82bd7

📥 Commits

Reviewing files that changed from the base of the PR and between e2c9f28 and 88dd7ad.

⛔ Files ignored due to path filters (9)
  • openmeter/ent/db/chargeusagebasedruns.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns/chargeusagebasedruns.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/chargeusagebasedruns_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (6)
  • openmeter/billing/charges/usagebased/adapter/detailedline.go
  • openmeter/billing/charges/usagebased/adapter/detailedline_test.go
  • openmeter/billing/charges/usagebased/adapter/realizationrun.go
  • openmeter/ent/schema/chargesusagebased.go
  • tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.down.sql
  • tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.up.sql
✅ Files skipped from review due to trivial changes (2)
  • openmeter/ent/schema/chargesusagebased.go
  • tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.up.sql
🚧 Files skipped from review as they are similar to previous changes (2)
  • tools/migrate/migrations/20260429140129_add_usage_based_run_detailed_lines_present.down.sql
  • openmeter/billing/charges/usagebased/adapter/realizationrun.go

@turip turip merged commit f3eec6f into main Apr 29, 2026
28 of 29 checks passed
@turip turip deleted the refactor/detailed-line-persisted-flag branch April 29, 2026 15:35
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.

2 participants