Skip to content

⚡ Bolt: Parallelize sitemap data fetching with Promise.all#74

Open
anyulled wants to merge 4 commits intomainfrom
bolt/optimize-sitemap-generation-14581543466514914908
Open

⚡ Bolt: Parallelize sitemap data fetching with Promise.all#74
anyulled wants to merge 4 commits intomainfrom
bolt/optimize-sitemap-generation-14581543466514914908

Conversation

@anyulled
Copy link
Owner

@anyulled anyulled commented Mar 4, 2026

💡 What: Replaced sequential await loops inside the app/sitemap.ts generation function with parallel Promise.all fetching.
🎯 Why: The original implementation iterated over years using a for...of loop and then sequentially awaited getSpeakers and getTalks internally. This created a performance bottleneck during the build time since the fetching of resources across different years and even within a single year was blocked until the previous fetch completed.
📊 Impact: Reduces total wait time for data fetching significantly during static sitemap generation by parallelizing operations.
🔬 Measurement: Inspect the build time locally or check the static rendering time of sitemap.xml.


PR created automatically by Jules for task 14581543466514914908 started by @anyulled

Summary by CodeRabbit

  • Refactor

    • Sitemap generation rewritten to build per-year lists and fetch data in parallel for faster, more reliable builds.
  • Documentation

    • Added a diary entry with guidance on async patterns and Promise-based error handling.
  • Style

    • Updated scorecard report formatting and footer; added newline to badge file.
  • Chores

    • Reformatted CI workflow YAML; removed two package override entries.
  • Tests

    • Updated a Cypress selector assertion for the home hero section.

Replaces sequential awaits in the sitemap generation loop with
Promise.all for fetching data. This parallelizes fetching multiple
resources and across multiple years, improving generation speed.

Co-authored-by: anyulled <100741+anyulled@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.

@vercel
Copy link

vercel bot commented Mar 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
devbcn Ready Ready Preview, Comment Mar 4, 2026 9:41am

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88039a05-5f80-41fa-aa09-eb4c0e8a8d49

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors sitemap generation to aggregate per-year URLs via parallelized Promise.all calls (with defensive .catch fallbacks), adds a diary entry documenting the change, reformats the GitHub Actions workflow YAML, updates scorecard report formatting, adjusts a Cypress selector, and removes two package override entries.

Changes

Cohort / File(s) Summary
Sitemap Generation Logic
app/sitemap.ts
Reworked per-year URL aggregation to build local yearUrls, fetch getSpeakers and getTalks in parallel via Promise.all(...).catch(()=>[]), and aggregate results with a final Promise.all + flattening instead of sequential awaits and direct pushes.
Documentation / Notes
.jules/bolt.md
Added a diary entry describing the sequential-awaits issue and the Promise.all per-year solution; no code changes.
CI Workflow
.github/workflows/scorecard.yml
YAML reformat: indentation and alignment normalized under on: and jobs.scorecard; no semantic changes.
Scorecard Files
scorecard-report.md, scorecard-badge.json
Adjusted Scorecard report table formatting and footer styling; added trailing newline to JSON.
Tests
cypress/e2e/home/home-editions.cy.ts
Updated selector assertion: now checks for at least two .hero8-header__event-line elements instead of h5 elements.
Package Overrides
package.json
Removed minimatch and ajv entries from the overrides object.

Sequence Diagram(s)

sequenceDiagram
    participant Sitemap as Sitemap.fn
    participant Speakers as getSpeakers(year)
    participant Talks as getTalks(year)
    participant Aggregator as YearAggregator

    Sitemap->>Aggregator: for each year -> create local yearUrls
    par Parallel fetch for year
        Sitemap->>Speakers: request speakers for year
        Sitemap->>Talks: request talks for year
    and
        Speakers-->>Sitemap: speakers[] (or [] on .catch)
        Talks-->>Sitemap: talks[] (or [] on .catch)
    end
    Sitemap->>Aggregator: push root, speaker and talk URLs into yearUrls
    Aggregator->>Sitemap: return yearUrls
    Sitemap->>Sitemap: Promise.all(yearPromises) -> flatten -> return full sitemap
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I hopped through loops with tiny paws,
Found awaits that paused because,
I grouped the years and called in pairs,
Promise.all freed time and cares,
Now sitemaps race — a rabbit's applause! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 summarizes the primary change: parallelizing sitemap data fetching with Promise.all in app/sitemap.ts, which is the main focus of the PR objectives and the largest functional change.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bolt/optimize-sitemap-generation-14581543466514914908

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.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the performance of sitemap generation by refactoring data fetching logic to utilize parallel execution. By moving from sequential await operations to Promise.all, the build process will experience reduced wait times, particularly when handling multiple years and diverse data sources, leading to a more efficient and robust sitemap build.

Highlights

  • Parallelized Sitemap Data Fetching: Replaced sequential await calls for getSpeakers and getTalks within the app/sitemap.ts file with Promise.all to fetch data concurrently across different years.
  • Improved Build Performance: Addressed a performance bottleneck during sitemap generation by enabling parallel execution of data fetching operations, significantly reducing total wait time.
  • Enhanced Robustness: Implemented .catch(() => []) for Promise.all calls to prevent individual data fetching failures from crashing the entire sitemap build process, ensuring resilience.
  • Documentation Update: Updated .jules/bolt.md to reflect the learning and action taken regarding sequential awaits in server files and the solution implemented for performance improvement.
Changelog
  • .jules/bolt.md
    • Added a new entry documenting the performance issue with sequential await calls in server files and the solution implemented.
  • app/sitemap.ts
    • Refactored the main sitemap generation function to use Promise.all for concurrent data fetching.
    • Modified loops to collect year-specific URLs into temporary arrays before combining them.
    • Integrated error handling with .catch(() => []) for data fetching promises.
  • scorecard-badge.json
    • Adjusted file ending to ensure consistent formatting.
  • scorecard-report.md
    • Formatted markdown tables and removed extraneous blank lines.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/scorecard.yml
Activity
  • No human activity has been recorded on this pull request since its creation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively refactors the sitemap generation logic to parallelize data fetching using Promise.all, which should significantly improve build performance. The implementation is solid. I've added a couple of suggestions to further improve the code's clarity and conciseness by removing redundant error handling and using the .flat() method for array manipulation. Overall, this is a great performance enhancement.

}

const speakers = await getSpeakers(year);
const [speakers, sessionGroups] = await Promise.all([getSpeakers(year).catch(() => []), getTalks(year).catch(() => [])]);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The .catch(() => []) calls are redundant here. The getSpeakers and getTalks functions already have internal try/catch blocks that handle errors by logging them and returning an empty array. As a result, these promises will always resolve and never reject. Removing the .catch() handlers will make the code cleaner and clearer without changing its behavior.

Suggested change
const [speakers, sessionGroups] = await Promise.all([getSpeakers(year).catch(() => []), getTalks(year).catch(() => [])]);
const [speakers, sessionGroups] = await Promise.all([getSpeakers(year), getTalks(year)]);

Comment on lines +88 to 90
for (const yearUrls of yearUrlsArrays) {
urls.push(...yearUrls);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

You can simplify this loop by using the .flat() method to flatten the array of arrays and then spreading the result into urls.push(). This is more concise and idiomatic.

  urls.push(...yearUrlsArrays.flat());

Copy link

@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.

🧹 Nitpick comments (2)
app/sitemap.ts (2)

31-31: Consider bounded concurrency for long-term stability.

Line 31 fans out all years at once. As editions grow, adding a small concurrency cap can prevent burst load/rate-limit spikes during sitemap generation.

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

In `@app/sitemap.ts` at line 31, The current years.map(async (year) => ...)
(creating yearPromises) fans out all years concurrently; change this to use
bounded concurrency by wrapping the per-year async task with a concurrency
limiter (e.g., p-limit or an internal semaphore/worker pool) and a small
CONCURRENCY constant, then build yearPromises via years.map(year => limit(() =>
theExistingPerYearAsyncFunction(year))) or via chunked/for-loop dispatch to
ensure only CONCURRENCY promises run at once; update references to yearPromises
so the rest of the sitemap generation awaits the limited set as before.

51-52: Remove redundant .catch(() => []) wrappers around getSpeakers/getTalks.

At Line 51, these catches duplicate existing fallback behavior and reduce error visibility if those functions change later.

♻️ Suggested simplification
-    const [speakers, sessionGroups] = await Promise.all([getSpeakers(year).catch(() => []), getTalks(year).catch(() => [])]);
+    const [speakers, sessionGroups] = await Promise.all([getSpeakers(year), getTalks(year)]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/sitemap.ts` around lines 51 - 52, Remove the redundant .catch(() => [])
wrappers around getSpeakers and getTalks in the Promise.all call: replace
Promise.all([getSpeakers(year).catch(() => []), getTalks(year).catch(() => [])])
with Promise.all([getSpeakers(year), getTalks(year)]), letting existing
fallback/error handling in getSpeakers/getTalks or their callers handle
failures; update references to the destructured variables speakers and
sessionGroups accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/sitemap.ts`:
- Line 31: The current years.map(async (year) => ...) (creating yearPromises)
fans out all years concurrently; change this to use bounded concurrency by
wrapping the per-year async task with a concurrency limiter (e.g., p-limit or an
internal semaphore/worker pool) and a small CONCURRENCY constant, then build
yearPromises via years.map(year => limit(() =>
theExistingPerYearAsyncFunction(year))) or via chunked/for-loop dispatch to
ensure only CONCURRENCY promises run at once; update references to yearPromises
so the rest of the sitemap generation awaits the limited set as before.
- Around line 51-52: Remove the redundant .catch(() => []) wrappers around
getSpeakers and getTalks in the Promise.all call: replace
Promise.all([getSpeakers(year).catch(() => []), getTalks(year).catch(() => [])])
with Promise.all([getSpeakers(year), getTalks(year)]), letting existing
fallback/error handling in getSpeakers/getTalks or their callers handle
failures; update references to the destructured variables speakers and
sessionGroups accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 836134e3-6d0e-4d94-8b3a-d82fb9af8f88

📥 Commits

Reviewing files that changed from the base of the PR and between fca6a1c and e98a5ef.

📒 Files selected for processing (5)
  • .github/workflows/scorecard.yml
  • .jules/bolt.md
  • app/sitemap.ts
  • scorecard-badge.json
  • scorecard-report.md

- Fixed `cypress/e2e/home/home-editions.cy.ts` to query `.hero8-header__event-line` instead of `h5` to match the actual DOM.
- Removed conflicting `ajv` and `minimatch` overrides in `package.json` that broke `npm ci` for testing and linting tools.

Co-authored-by: anyulled <100741+anyulled@users.noreply.github.com>
- Fixed `cypress/e2e/home/home-editions.cy.ts` to query `.hero8-header__event-line` instead of `h5` to match the actual DOM.
- Removed conflicting `ajv` and `minimatch` overrides in `package.json` that broke `npm ci` for testing and linting tools.
- Re-ran `npm install` to update `package-lock.json` properly so `npm ci` doesn't fail.

Co-authored-by: anyulled <100741+anyulled@users.noreply.github.com>
- Fixed `cypress/e2e/home/home-editions.cy.ts` to query `.hero8-header__event-line` instead of `h5` to match the actual DOM.
- Removed conflicting `ajv` and `minimatch` overrides in `package.json` that broke `npm ci` for testing and linting tools.
- Re-ran `npm install` to update `package-lock.json` properly so `npm ci` doesn't fail.
- Fixed scorecard format failures.

Co-authored-by: anyulled <100741+anyulled@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