Skip to content

Reduce Stripe queries - #710

Merged
epompeii merged 3 commits into
develfrom
u/ep/customer-id
Mar 18, 2026
Merged

Reduce Stripe queries#710
epompeii merged 3 commits into
develfrom
u/ep/customer-id

Conversation

@epompeii

Copy link
Copy Markdown
Member

This changeset reduces the number of Stripe queries down to two each for Metrics and Runner Minutes.

@github-actions

github-actions Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

🤖 Claude Code Review

PR: #710
Base: devel
Head: u/ep/customer-id
Commit: 218af6570e8061120621323a9b0d9a32986c1633


Now I have a thorough picture of the changes. Here's my review:


PR Review: customer_id — Use Stripe CustomerId for metered billing

Commits: 4 commits (customer_id, Update runner timeouts, customer_id #711, record_usage_tests)

Summary

This PR eliminates an extra Stripe API call per billing event by resolving MeteredPlanId (subscription ID) to CustomerId upfront, then passing CustomerId directly to record_metered_usage. Previously, every call to record_metered_usage fetched the subscription from Stripe just to extract the customer ID.

Positive

  • Performance win: Removes a redundant get_subscription API call on every metrics/runner usage recording. This is significant for runner billing which fires on every heartbeat.
  • Good caching in channel.rs: The CachedCustomer / BillingState pattern now also caches the plan-active check, avoiding repeated Stripe calls during a job's lifetime.
  • Better test coverage: Tests now assert on the meter event payload (event_name, METER_CUSTOMER_KEY, METER_VALUE_KEY) instead of fire-and-forget, and runner usage recording is now tested.
  • Feature gating cleanup: Moving dep:bencher_billing from sentry to plus in api_runners/Cargo.toml is correct — billing is a plus feature, not a sentry concern.

Issues

1. Test uses .into() instead of a validated parse — job.rs:493

PlanKind::Metered("cus_test".into())

Per CLAUDE.md: "Always pass strong types... even in tests." The old code used .parse::<MeteredPlanId>().unwrap(). Using .into() on a string literal bypasses any validation that CustomerId may have. If CustomerId has a FromStr impl, prefer .parse().unwrap() to ensure the test value is valid.

2. get_metered_plan_status return type is a bare tuple — biller.rs:628

) -> Result<(PlanStatus, CustomerId), BillingError> {

A (PlanStatus, CustomerId) tuple is opaque at call sites. Consider a small named struct (e.g., MeteredPlanInfo) for clarity, especially since this is a public API used across crates. This is minor but worth considering if more fields are added later.

3. customer_id in channel.rs now does a Stripe API call inside auth_conn! scope

In BillingState::customer_id (channel.rs:~476-492), the code holds an auth_conn! database connection, then awaits biller.get_metered_plan_status() (a Stripe HTTP call) before releasing it. If the DB connection is held across the await point, this could increase connection pool pressure under load. Verify that auth_conn! returns an owned connection (not a pool guard held across awaits).

4. Plan status check added to runner billing path — subtle behavior change

Previously, BillingState only cached the MeteredPlanId from the DB — it didn't check whether the plan was active. Now it calls get_metered_plan_status and returns None for inactive plans. This means runner billing silently stops if a plan becomes inactive mid-job. This is likely intentional but worth confirming — should an inactive plan during a running job trigger a warning or job termination instead of silent no-billing?

5. #[expect(clippy::too_many_arguments)]biller.rs:788

The metered_subscription test helper now takes 8 arguments. Rather than suppressing the lint, consider grouping related params into a struct (e.g., subscription details). Low priority since it's test code.

Nits

  • The record_metrics_usage test helper changed from looping usage_count times with random quantities to a single call with one quantity. This reduces test breadth slightly but the explicit assertion on the event payload is a net improvement.

Verdict

The core optimization is sound and well-implemented. The main concern is item 3 (potential connection held across an async Stripe call). If that's confirmed safe, this looks good to merge.


Model: claude-opus-4-6

@github-actions

github-actions Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchu/ep/customer-id
Testbedubuntu-22.04
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
microseconds (µs)
(Result Δ%)
Upper Boundary
microseconds (µs)
(Limit %)
Adapter::Json📈 view plot
🚷 view threshold
3.73 µs
(+6.99%)Baseline: 3.48 µs
4.63 µs
(80.45%)
Adapter::Magic (JSON)📈 view plot
🚷 view threshold
3.68 µs
(+6.06%)Baseline: 3.47 µs
4.54 µs
(80.93%)
Adapter::Magic (Rust)📈 view plot
🚷 view threshold
25.52 µs
(-0.52%)Baseline: 25.65 µs
31.05 µs
(82.19%)
Adapter::Rust📈 view plot
🚷 view threshold
2.77 µs
(-2.65%)Baseline: 2.85 µs
3.32 µs
(83.34%)
Adapter::RustBench📈 view plot
🚷 view threshold
2.74 µs
(-3.50%)Baseline: 2.84 µs
3.31 µs
(82.96%)
head_version_insert/batch/10📈 view plot
🚷 view threshold
100.04 µs
(-0.14%)Baseline: 100.18 µs
120.03 µs
(83.35%)
head_version_insert/batch/100📈 view plot
🚷 view threshold
234.73 µs
(-1.08%)Baseline: 237.29 µs
266.28 µs
(88.15%)
head_version_insert/batch/255📈 view plot
🚷 view threshold
456.26 µs
(-1.05%)Baseline: 461.10 µs
491.62 µs
(92.81%)
head_version_insert/batch/50📈 view plot
🚷 view threshold
157.66 µs
(-1.72%)Baseline: 160.42 µs
182.01 µs
(86.62%)
threshold_query/join/10📈 view plot
🚷 view threshold
145.27 µs
(+0.62%)Baseline: 144.38 µs
169.46 µs
(85.72%)
threshold_query/join/20📈 view plot
🚷 view threshold
155.08 µs
(-2.20%)Baseline: 158.58 µs
185.41 µs
(83.64%)
threshold_query/join/5📈 view plot
🚷 view threshold
133.56 µs
(-2.10%)Baseline: 136.42 µs
159.27 µs
(83.86%)
threshold_query/join/50📈 view plot
🚷 view threshold
195.33 µs
(-2.35%)Baseline: 200.02 µs
232.12 µs
(84.15%)
🐰 View full continuous benchmarking report in Bencher

epompeii and others added 2 commits March 18, 2026 02:14
Co-authored-by: Claude <noreply@anthropic.com>
@epompeii
epompeii merged commit de5f959 into devel Mar 18, 2026
61 checks passed
@epompeii
epompeii deleted the u/ep/customer-id branch March 18, 2026 04:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant