Skip to content

⚡ Bolt: optimize API calls and vector operations#192

Open
Moohan wants to merge 2 commits intomainfrom
bolt-performance-opts-7840956573680910429
Open

⚡ Bolt: optimize API calls and vector operations#192
Moohan wants to merge 2 commits intomainfrom
bolt-performance-opts-7840956573680910429

Conversation

@Moohan
Copy link
Owner

@Moohan Moohan commented Mar 22, 2026

💡 What:

  • Replaced scalar ifelse() with standard if/else in get_meter_details to prevent redundant get_meter_gsp() calls for gas meters.
  • Introduced include_gsp parameter to get_meter_details to skip GSP lookups in get_consumption and get_meter_gsp.
  • Replaced ifelse() with logical indexing (x[is.na(x)] <- 0) in combine_consumption for efficient NA handling.

🎯 Why:

  • R's ifelse() evaluates both branches, causing unnecessary API calls even for scalar logic where the condition is always FALSE for certain meter types.
  • Logical indexing is significantly faster and more memory-efficient than ifelse() for large vectors, which is common in consumption data.
  • Reducing redundant API calls improves responsiveness and reduces network overhead.

📊 Impact:

  • get_meter_details('gas') now makes 0 API calls (down from 1).
  • combine_consumption NA replacement is ~3-4x faster and uses ~70% less memory for 100k rows.

🔬 Measurement:

  • Verified API call counts using a mock octopus_api.
  • Benchmarked ifelse vs logical indexing using bench::mark():
    • ifelse: 3.6ms median, 10.68MB mem_alloc
    • logical_indexing: 1.16ms median, 3.13MB mem_alloc (on 100k elements)

PR created automatically by Jules for task 7840956573680910429 started by @Moohan

Summary by Sourcery

Optimize meter detail retrieval and consumption combination to reduce redundant API calls and improve vector performance.

Bug Fixes:

  • Prevent unnecessary GSP lookups when fetching gas meter details or when GSP is not required.

Enhancements:

  • Add an include_gsp flag to meter detail retrieval to control Grid Supply Point lookups and avoid circular calls.
  • Switch NA replacement in combined consumption data from ifelse() to logical indexing for better performance and memory usage.
  • Adjust get_consumption and get_meter_gsp defaults to leverage the new include_gsp parameter and avoid redundant meter lookups.

Documentation:

  • Document the include_gsp parameter in meter detail and GSP retrieval documentation.
  • Capture performance learnings and guidelines for NA handling and scalar conditionals in internal Bolt notes.

Summary by CodeRabbit

  • New Features

    • Added an optional parameter to control meter-details retrieval behaviour for more flexible lookups.
  • Performance Improvements

    • Improved NA-handling in consumption processing for reduced memory use and faster execution.
  • Documentation

    • Added guidance on using logical indexing versus vectorised conditionals for performance; updated usage docs to match behaviour.
  • Chores

    • Build configuration updated to ignore internal documentation artifacts.

- Replace scalar `ifelse` with `if/else` in `get_meter_details` to avoid redundant API calls.
- Add `include_gsp` parameter to `get_meter_details` to skip GSP lookup in `get_consumption`.
- Replace `ifelse` with logical indexing in `combine_consumption` for ~3-4x speedup in NA replacement.

Co-authored-by: Moohan <5982260+Moohan@users.noreply.github.com>
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 22, 2026

Reviewer's Guide

Optimizes meter-related helpers and consumption aggregation to avoid redundant GSP API calls and to speed up NA handling in consumption vectors, primarily by adding an include_gsp flag to get_meter_details and replacing ifelse() calls with short-circuiting conditionals and logical indexing.

Sequence diagram for meter details and GSP lookup with include_gsp flag

sequenceDiagram
  actor Developer
  participant get_consumption
  participant get_meter_details
  participant get_meter_gsp
  participant octopus_api

  Developer->>get_consumption: call(meter_type, direction, mpan_mprn=NULL, serial_number=NULL)
  get_consumption->>get_meter_details: meter_type, direction, include_gsp=false

  alt meter_type_is_electricity_and_include_gsp_true
    get_meter_details->>get_meter_gsp: mpan_mprn
    get_meter_gsp->>octopus_api: fetch_GSP_for_mpan
    octopus_api-->>get_meter_gsp: GSP
    get_meter_gsp-->>get_meter_details: GSP
    get_meter_details-->>get_consumption: meter_details_with_gsp
  else include_gsp_false_or_meter_type_gas
    get_meter_details-->>get_consumption: meter_details_with_gsp_set_to_NA
  end

  Developer->>get_meter_gsp: call(mpan_missing)
  get_meter_gsp->>get_meter_details: meter_type=electricity, direction=NULL, include_gsp=false
  get_meter_details-->>get_meter_gsp: meter_details_with_mpan_and_gsp_NA
  get_meter_gsp->>octopus_api: fetch_GSP_for_mpan
  octopus_api-->>get_meter_gsp: GSP
  get_meter_gsp-->>Developer: GSP
Loading

Flow diagram for NA replacement in combine_consumption

flowchart TD
  A[Start combine_consumption] --> B[Set import_consumption from consumption_import]
  B --> C[Set export_consumption from consumption_export]
  C --> D[Identify NA elements in import_consumption]
  D --> E[Set NA positions in import_consumption to 0]
  E --> F[Identify NA elements in export_consumption]
  F --> G[Set NA positions in export_consumption to 0]
  G --> H[Remove consumption_import and consumption_export columns]
  H --> I[Return updated result]
Loading

File-Level Changes

Change Details Files
Add include_gsp parameter to get_meter_details to control GSP lookup and avoid unnecessary API calls.
  • Extend get_meter_details signature with include_gsp logical parameter defaulting to TRUE and document it in the roxygen comments.
  • Initialize gsp to NA_character_ and only call get_meter_gsp when meter_type is electricity and include_gsp is TRUE.
  • Update construction of the octopus_meter-point object to use the precomputed gsp value instead of an inline ifelse().
R/meter_details.R
Ensure downstream callers skip GSP lookup when it will be fetched separately, preventing double calls.
  • Change get_consumption to call get_meter_details with include_gsp = FALSE when inferring meter identifiers.
  • Update get_meter_gsp default mpan argument to obtain the MPAN from get_meter_details with include_gsp = FALSE, and regenerate the Rd usage accordingly.
R/get_consumption.R
R/get_meter_gsp.R
man/get_meter_gsp.Rd
Optimize NA replacement in combined consumption data using logical indexing instead of ifelse().
  • Replace ifelse-based NA replacement for import and export consumption with direct assignment followed by in-place NA replacement using logical indexing.
  • Keep column renaming/cleanup logic the same after the more efficient NA handling.
R/meter_details.R
Capture performance learnings about ifelse vs logical indexing in project automation documentation.
  • Add a Bolt note describing benchmarking results and guidance to prefer logical indexing over ifelse for NA replacement and to use if/else for scalar logic with side-effecting operations.
.jules/bolt.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d157ef10-9a17-43d9-863d-ec96c72997a8

📥 Commits

Reviewing files that changed from the base of the PR and between 583caad and 3a9491f.

📒 Files selected for processing (1)
  • R/get_consumption.R

Walkthrough

Adds an include_gsp parameter to get_meter_details() (default TRUE) to optionally skip GSP lookups, updates dependent callers to avoid GSP fetches by default, and replaces ifelse() NA-to-zero conversions with logical-index assignment in consumption combining. Also adds .jules to .Rbuildignore and a new .jules/bolt.md note.

Changes

Cohort / File(s) Summary
Build & Ignore
\.Rbuildignore
Added ignore pattern for ^\.jules$.
Notes & Docs
.jules/bolt.md, man/get_meter_gsp.Rd
Added learning note on ifelse() vs logical indexing; updated get_meter_gsp() usage doc to show get_meter_details("electricity", include_gsp = FALSE) as the default expression.
Core Meter Details Logic
R/meter_details.R
Added exported parameter include_gsp = TRUE to get_meter_details(); conditional GSP assignment now respects include_gsp; replaced ifelse(is.na(...), 0, ...) with logical-index assignment for consumption NA-to-zero handling in combine_consumption().
Call Sites / Consumers
R/get_consumption.R, R/get_meter_gsp.R
Updated default calls to get_meter_details(..., include_gsp = FALSE) when filling missing meter identifiers or defaulting mpan.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ 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 directly relates to the main objectives of the pull request: optimizing API calls by adding include_gsp parameter and improving vector operations by replacing ifelse() with logical indexing, as described in the PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bolt-performance-opts-7840956573680910429

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

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In get_meter_details, the gsp field for gas meters changes from a logical NA to NA_character_; double-check downstream consumers don’t rely on the previous type (e.g. is.logical(gsp) or strict type checks) and, if needed, coerce consistently for both electricity and gas.
  • The new include_gsp argument is silently ignored for gas meters; consider documenting this behavior explicitly in the function comments or asserting that it only applies when meter_type == "electricity" to avoid confusion for callers.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `get_meter_details`, the `gsp` field for gas meters changes from a logical `NA` to `NA_character_`; double-check downstream consumers don’t rely on the previous type (e.g. `is.logical(gsp)` or strict type checks) and, if needed, coerce consistently for both electricity and gas.
- The new `include_gsp` argument is silently ignored for gas meters; consider documenting this behavior explicitly in the function comments or asserting that it only applies when `meter_type == "electricity"` to avoid confusion for callers.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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

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

Inline comments:
In @.jules/bolt.md:
- Line 1: Change the top-level heading in .jules/bolt.md from a level-2 to a
level-1 heading by replacing the leading "##" with "#" on the first line (the
line containing "2025-05-15 - Vectorized ifelse() vs Logical Indexing for NAs")
so the file starts with a proper top-level heading.

In `@R/get_consumption.R`:
- Line 78: The call to get_meter_details is exceeding the 80-character line
limit; update the invocation in get_consumption (the meter_details assignment)
to wrap arguments across multiple lines so each argument (meter_type, direction,
include_gsp = FALSE) sits on its own indented line within the parentheses,
keeping the opening and closing parentheses aligned and staying under 80
characters per line.

In `@R/meter_details.R`:
- Around line 88-92: The is_testing() branch ignores the new include_gsp
parameter by returning testing_meter(meter_type) unconditionally; update the
is_testing() path to honor include_gsp (either call testing_meter with
include_gsp if that helper accepts it, e.g., testing_meter(meter_type,
include_gsp), or apply the same include_gsp filtering after testing_meter()
returns) so behavior matches the non-testing branch; touch the function
containing meter_type/direction/include_gsp and the is_testing()/testing_meter
call sites to ensure include_gsp is applied consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7453cdfe-2686-4a12-b65d-d8b273397ef0

📥 Commits

Reviewing files that changed from the base of the PR and between 74f7003 and 583caad.

📒 Files selected for processing (6)
  • .Rbuildignore
  • .jules/bolt.md
  • R/get_consumption.R
  • R/get_meter_gsp.R
  • R/meter_details.R
  • man/get_meter_gsp.Rd

@@ -0,0 +1,5 @@
## 2025-05-15 - Vectorized ifelse() vs Logical Indexing for NAs
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 | 🟡 Minor

Use a top-level heading on the first line.

Markdown lint warns because the file starts with ## instead of #.

Suggested fix
-## 2025-05-15 - Vectorized ifelse() vs Logical Indexing for NAs
+# 2025-05-15 - Vectorized ifelse() vs Logical Indexing for NAs
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## 2025-05-15 - Vectorized ifelse() vs Logical Indexing for NAs
# 2025-05-15 - Vectorized ifelse() vs Logical Indexing for NAs
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

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

In @.jules/bolt.md at line 1, Change the top-level heading in .jules/bolt.md
from a level-2 to a level-1 heading by replacing the leading "##" with "#" on
the first line (the line containing "2025-05-15 - Vectorized ifelse() vs Logical
Indexing for NAs") so the file starts with a proper top-level heading.

Comment on lines +88 to +92
function(
meter_type = c("electricity", "gas"),
direction = NULL,
include_gsp = TRUE
) {
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 | 🟡 Minor

include_gsp is not applied in the testing branch.

The new parameter is introduced in the signature, but when is_testing() is true the function returns testing_meter(meter_type) directly, so include_gsp = FALSE is ignored. That creates inconsistent behaviour across environments.

Suggested fix
   if (is_testing()) {
-      testing_meter(meter_type)
+      meter <- testing_meter(meter_type)
+      if (meter_type == "electricity" && isFALSE(include_gsp)) {
+        meter$gsp <- NA_character_
+      }
+      meter
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@R/meter_details.R` around lines 88 - 92, The is_testing() branch ignores the
new include_gsp parameter by returning testing_meter(meter_type)
unconditionally; update the is_testing() path to honor include_gsp (either call
testing_meter with include_gsp if that helper accepts it, e.g.,
testing_meter(meter_type, include_gsp), or apply the same include_gsp filtering
after testing_meter() returns) so behavior matches the non-testing branch; touch
the function containing meter_type/direction/include_gsp and the
is_testing()/testing_meter call sites to ensure include_gsp is applied
consistently.

- Replace scalar `ifelse` with `if/else` in `get_meter_details` to avoid redundant API calls.
- Add `include_gsp` parameter to `get_meter_details` to skip GSP lookup in `get_consumption`.
- Replace `ifelse` with logical indexing in `combine_consumption` for ~3-4x speedup in NA replacement.
- Fix linting error by breaking long line in `R/get_consumption.R`.

Co-authored-by: Moohan <5982260+Moohan@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant