Skip to content

perf(analysis): paginate before hydration to fix component search tim…#2411

Open
mrrajan wants to merge 1 commit into
guacsec:release/0.4.zfrom
mrrajan:TC-4365-release-0.4.z
Open

perf(analysis): paginate before hydration to fix component search tim…#2411
mrrajan wants to merge 1 commit into
guacsec:release/0.4.zfrom
mrrajan:TC-4365-release-0.4.z

Conversation

@mrrajan

@mrrajan mrrajan commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Apply LIMIT/OFFSET at the graph node filtering stage so only the requested page of nodes runs through expensive ancestor/descendant hydration. Previously all matching nodes were fully hydrated then sliced with paginate_array.

  • Add count_matching_nodes, collect_graph_paged, run_graph_query_paged
  • Use BTreeSet for sorted unique SBOM IDs and order_by_asc for deterministic pagination ordering
  • Update retrieve, retrieve_single, retrieve_latest to use paged path
  • Add pagination endpoint and service-level tests

Implements TC-4365

Assisted-by: Claude Code

Summary by Sourcery

Apply server-side pagination at the graph node filtering stage to avoid hydrating non-requested nodes and ensure deterministic ordering across SBOM graphs.

New Features:

  • Add paginated graph query path that counts matching nodes and returns paged hydrated results.
  • Expose pagination behavior through the analysis retrieval HTTP endpoint.

Enhancements:

  • Use deterministic ordering of SBOM graphs and nodes for stable pagination results.
  • Update component retrieval paths to use the new pre-hydration pagination workflow.

Tests:

  • Add service-level tests verifying pagination limits hydration and offset/limit combinations.
  • Add endpoint tests to validate paginated responses, totals, and page-to-page differences.

@sourcery-ai

sourcery-ai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Apply pagination at the graph node index level so only the requested page of nodes is hydrated, while ensuring deterministic ordering across SBOM graphs and exposing pagination-aware retrieval endpoints with tests.

Sequence diagram for paginated graph query with pre-hydration pagination

sequenceDiagram
    actor Client
    participant Endpoint as AnalysisEndpoint
    participant Service as AnalysisService
    participant DB as ConnectionTrait
    participant Loader as GraphLoader
    participant Collector

    Client->>Endpoint: retrieve / retrieve_single / retrieve_latest
    Endpoint->>Service: run_graph_query_paged(query, options, graphs, paginated, connection)

    Service->>Service: count_matching_nodes(query, graphs)
    Service->>Service: collect_graph_paged(query, graphs, concurrency, offset, limit, create)

    loop for each paged node
        Service->>Loader: GraphLoader::new(self.clone())
        Service->>Collector: Collector::new(..., Direction::Incoming, ...).collect()
        Service->>Collector: Collector::new(..., Direction::Outgoing, ...).collect()
        Collector-->>Service: ancestors, descendants
        Service-->Service: build Node { base, ancestors, descendants, warnings }
    end

    Service-->>Endpoint: PaginatedResults<Node> { items, total }
    Endpoint-->>Client: HTTP response with items and total
Loading

File-Level Changes

Change Details Files
Introduce pagination-aware graph query execution that counts matches and hydrates only the requested page of nodes.
  • Add count_matching_nodes to count filtered node indices across all loaded graphs without hydration.
  • Add collect_graph_paged to flatten matching node indices across graphs, sort them by (sbom_id, node_id), apply offset/limit, and hydrate only the paged subset with bounded concurrency.
  • Add run_graph_query_paged to orchestrate counting, pre-hydration pagination, and node hydration, returning PaginatedResults with items and total.
modules/analysis/src/service/mod.rs
Update retrieval paths to use the new paginated graph query and return total counts instead of array-slicing results in memory.
  • Change retrieve to call run_graph_query_paged and drop paginate_array-based slicing.
  • Change retrieve_single and retrieve_latest-style paths to load graphs and then use run_graph_query_paged for consistent pagination semantics.
  • Ensure zero-limit handling and early return when offset exceeds total to avoid unnecessary work.
modules/analysis/src/service/mod.rs
Stabilize and determinize pagination ordering across SBOM graphs.
  • Change latest graph selection to use BTreeSet for latest_ids so SBOM IDs are kept sorted.
  • Order sbom::Entity queries by ascending SbomId when loading distinct SBOM graphs.
  • Use sorted (sbom_id, node_id) ordering in collect_graph_paged to ensure deterministic cross-graph pagination.
modules/analysis/src/service/load/mod.rs
modules/analysis/src/service/mod.rs
Add service-level and endpoint-level tests to validate pagination behavior and totals.
  • Add test_pagination_limits_hydration to ensure limit controls hydrated item count while total reflects all matching nodes.
  • Add test_pagination_offset to verify different offsets return different items and combined pages match totals.
  • Add test_pagination_through_endpoint to assert HTTP responses expose total and per-page items, and that successive pages contain distinct items.
modules/analysis/src/service/test/mod.rs
modules/analysis/src/endpoints/tests/mod.rs

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

@mrrajan mrrajan marked this pull request as ready for review June 23, 2026 09:13

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • Both count_matching_nodes and collect_graph_paged independently iterate and filter all nodes across graphs; consider factoring out a shared iterator or precomputed list so you don’t double-walk and re-apply filter for the same query.
  • collect_graph_paged sorts the matching nodes but then uses buffer_unordered, which can re-order the hydrated results; if callers depend on deterministic (sbom_id, node_id) ordering per page, consider using an ordered concurrency pattern or re-sorting after hydration.
  • The limit == 0 branch in collect_graph_paged effectively means 'no limit'; if that semantic is relied on, it may be worth making it explicit in the type or docs, or normalizing Paginated earlier so limit is always non-zero when pagination is intended.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Both `count_matching_nodes` and `collect_graph_paged` independently iterate and filter all nodes across graphs; consider factoring out a shared iterator or precomputed list so you don’t double-walk and re-apply `filter` for the same query.
- `collect_graph_paged` sorts the matching nodes but then uses `buffer_unordered`, which can re-order the hydrated results; if callers depend on deterministic `(sbom_id, node_id)` ordering per page, consider using an ordered concurrency pattern or re-sorting after hydration.
- The `limit == 0` branch in `collect_graph_paged` effectively means 'no limit'; if that semantic is relied on, it may be worth making it explicit in the type or docs, or normalizing `Paginated` earlier so `limit` is always non-zero when pagination is intended.

## Individual Comments

### Comment 1
<location path="modules/analysis/src/service/test/mod.rs" line_range="856-886" />
<code_context>
+/// Verify that pagination limits the number of hydrated nodes.
+#[test_context(TrustifyContext)]
+#[test(tokio::test)]
+async fn test_pagination_limits_hydration(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
+    ctx.ingest_documents([
+        "spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
+        "spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
+    ])
+    .await?;
+
+    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());
+
+    // When: request with limit=1
+    let result = service
+        .retrieve(
+            &Query::q("spymemcached"),
+            QueryOptions::ancestors(),
+            Paginated {
+                offset: 0,
+                limit: 1,
+            },
+            &ctx.db,
+        )
+        .await?;
+
+    // Then: only 1 item returned but total reflects all matches
+    assert_eq!(result.items.len(), 1);
+    assert_eq!(result.total, 2);
+
+    Ok(())
+}
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add a service-level test for offset beyond total and limit=0 behavior

These tests don’t yet cover the branches where `offset >= total` and `limit == 0` in `run_graph_query_paged`. Please add a case where a second call uses an `offset``total` and asserts `items.is_empty()` while `total` is unchanged, and another case with `Paginated { offset: 0, limit: 0 }` that asserts all matching nodes are returned and `total` is correct.

```suggestion
/// Verify that pagination limits the number of hydrated nodes.
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_limits_hydration(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
    ctx.ingest_documents([
        "spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
        "spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
    ])
    .await?;

    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

    // When: request with limit=1
    let first_page = service
        .retrieve(
            &Query::q("spymemcached"),
            QueryOptions::ancestors(),
            Paginated {
                offset: 0,
                limit: 1,
            },
            &ctx.db,
        )
        .await?;

    // Then: only 1 item returned but total reflects all matches
    assert_eq!(first_page.items.len(), 1);
    assert_eq!(first_page.total, 2);

    // And when: requesting a page with offset beyond total
    let beyond_total_page = service
        .retrieve(
            &Query::q("spymemcached"),
            QueryOptions::ancestors(),
            Paginated {
                offset: first_page.total as i64,
                limit: 1,
            },
            &ctx.db,
        )
        .await?;

    // Then: no items are returned but total remains unchanged
    assert!(beyond_total_page.items.is_empty());
    assert_eq!(beyond_total_page.total, first_page.total);

    Ok(())
}

/// Verify that limit=0 returns all hydrated nodes while respecting total.
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_limit_zero_returns_all(
    ctx: &TrustifyContext,
) -> Result<(), anyhow::Error> {
    ctx.ingest_documents([
        "spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
        "spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
    ])
    .await?;

    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

    // When: request with limit=0
    let result = service
        .retrieve(
            &Query::q("spymemcached"),
            QueryOptions::ancestors(),
            Paginated {
                offset: 0,
                limit: 0,
            },
            &ctx.db,
        )
        .await?;

    // Then: all matching nodes are returned and total is correct
    assert_eq!(result.items.len(), 2);
    assert_eq!(result.total, 2);

    Ok(())
}
```
</issue_to_address>

### Comment 2
<location path="modules/analysis/src/service/test/mod.rs" line_range="891-853" />
<code_context>
+async fn test_pagination_offset(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
</code_context>
<issue_to_address>
**suggestion (testing):** Consider a test asserting deterministic ordering across repeated paginated calls

Given the new deterministic ordering via `(sbom_id, node_id)` and ordered SBOM loading, it would be helpful to add a test that calls `retrieve` twice with the same `Paginated { offset: 0, limit: 2 }` and asserts the same items, in the same order, are returned both times. This will catch regressions where ordering starts to depend on insertion order or map iteration.

Suggested implementation:

```rust
    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

    // Given: page 1 (offset=0, limit=1)
    let page1 = service

```

To implement the deterministic ordering test you requested, add a new async test next to `test_pagination_offset` (reusing the same ingestion and service setup). The new test should:

```rust
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_offset_deterministic_ordering(
    ctx: &TrustifyContext,
) -> Result<(), anyhow::Error> {
    // Given: same documents and service setup as test_pagination_offset
    ctx.ingest_documents([
        "spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
        "spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
    ])
    .await?;

    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

    // When: retrieve the first page twice with identical pagination
    let page1_first = service
        .retrieve(
            /* whatever query/filter arguments you use in test_pagination_offset, e.g.: */
            /* &some_query, */
            Paginated {
                offset: 0,
                limit: 2,
            },
            /* and any additional params such as sort/filter, if present */
        )
        .await?;

    let page1_second = service
        .retrieve(
            /* same arguments as above */
            /* &some_query, */
            Paginated {
                offset: 0,
                limit: 2,
            },
        )
        .await?;

    // Then: both calls should return the same total and the same items in the same order
    assert_eq!(page1_first.total, page1_second.total);
    assert_eq!(page1_first.items.len(), page1_second.items.len());
    assert_eq!(page1_first.items, page1_second.items);

    Ok(())
}
```

You will need to:

1. Copy the exact `retrieve` invocation used in `test_pagination_offset` (same query/filter arguments, additional parameters, etc.) for both `page1_first` and `page1_second` so that the only varying factor is the repeated call.
2. If `retrieve` requires additional context (e.g., a `query` struct, `AdvisoryQuery`, `ComponentQuery`, or feature flags), construct those exactly as in `test_pagination_offset` to ensure the test exercises the same path.
3. If `items` is not directly `Eq` (e.g., contains non-`Eq` types), instead compare the relevant deterministic fields (e.g., `(sbom_id, node_id)` for each item) in order:
   ```rust
   let first_keys: Vec<_> = page1_first.items.iter().map(|item| (item.sbom_id, item.node_id)).collect();
   let second_keys: Vec<_> = page1_second.items.iter().map(|item| (item.sbom_id, item.node_id)).collect();
   assert_eq!(first_keys, second_keys);
   ```
4. Place this new test function in `modules/analysis/src/service/test/mod.rs` near `test_pagination_offset` to keep pagination-related tests grouped.
</issue_to_address>

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.

Comment on lines +856 to +886
/// Verify that pagination limits the number of hydrated nodes.
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_limits_hydration(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
ctx.ingest_documents([
"spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
"spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
])
.await?;

let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

// When: request with limit=1
let result = service
.retrieve(
&Query::q("spymemcached"),
QueryOptions::ancestors(),
Paginated {
offset: 0,
limit: 1,
},
&ctx.db,
)
.await?;

// Then: only 1 item returned but total reflects all matches
assert_eq!(result.items.len(), 1);
assert_eq!(result.total, 2);

Ok(())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Add a service-level test for offset beyond total and limit=0 behavior

These tests don’t yet cover the branches where offset >= total and limit == 0 in run_graph_query_paged. Please add a case where a second call uses an offsettotal and asserts items.is_empty() while total is unchanged, and another case with Paginated { offset: 0, limit: 0 } that asserts all matching nodes are returned and total is correct.

Suggested change
/// Verify that pagination limits the number of hydrated nodes.
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_limits_hydration(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
ctx.ingest_documents([
"spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
"spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
])
.await?;
let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());
// When: request with limit=1
let result = service
.retrieve(
&Query::q("spymemcached"),
QueryOptions::ancestors(),
Paginated {
offset: 0,
limit: 1,
},
&ctx.db,
)
.await?;
// Then: only 1 item returned but total reflects all matches
assert_eq!(result.items.len(), 1);
assert_eq!(result.total, 2);
Ok(())
}
/// Verify that pagination limits the number of hydrated nodes.
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_limits_hydration(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
ctx.ingest_documents([
"spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
"spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
])
.await?;
let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());
// When: request with limit=1
let first_page = service
.retrieve(
&Query::q("spymemcached"),
QueryOptions::ancestors(),
Paginated {
offset: 0,
limit: 1,
},
&ctx.db,
)
.await?;
// Then: only 1 item returned but total reflects all matches
assert_eq!(first_page.items.len(), 1);
assert_eq!(first_page.total, 2);
// And when: requesting a page with offset beyond total
let beyond_total_page = service
.retrieve(
&Query::q("spymemcached"),
QueryOptions::ancestors(),
Paginated {
offset: first_page.total as i64,
limit: 1,
},
&ctx.db,
)
.await?;
// Then: no items are returned but total remains unchanged
assert!(beyond_total_page.items.is_empty());
assert_eq!(beyond_total_page.total, first_page.total);
Ok(())
}
/// Verify that limit=0 returns all hydrated nodes while respecting total.
#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_limit_zero_returns_all(
ctx: &TrustifyContext,
) -> Result<(), anyhow::Error> {
ctx.ingest_documents([
"spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
"spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
])
.await?;
let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());
// When: request with limit=0
let result = service
.retrieve(
&Query::q("spymemcached"),
QueryOptions::ancestors(),
Paginated {
offset: 0,
limit: 0,
},
&ctx.db,
)
.await?;
// Then: all matching nodes are returned and total is correct
assert_eq!(result.items.len(), 2);
assert_eq!(result.total, 2);
Ok(())
}

@@ -852,3 +852,100 @@ async fn resolve_sbom_cdx_rh_variant_external_node_sbom(

Ok(())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Consider a test asserting deterministic ordering across repeated paginated calls

Given the new deterministic ordering via (sbom_id, node_id) and ordered SBOM loading, it would be helpful to add a test that calls retrieve twice with the same Paginated { offset: 0, limit: 2 } and asserts the same items, in the same order, are returned both times. This will catch regressions where ordering starts to depend on insertion order or map iteration.

Suggested implementation:

    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

    // Given: page 1 (offset=0, limit=1)
    let page1 = service

To implement the deterministic ordering test you requested, add a new async test next to test_pagination_offset (reusing the same ingestion and service setup). The new test should:

#[test_context(TrustifyContext)]
#[test(tokio::test)]
async fn test_pagination_offset_deterministic_ordering(
    ctx: &TrustifyContext,
) -> Result<(), anyhow::Error> {
    // Given: same documents and service setup as test_pagination_offset
    ctx.ingest_documents([
        "spdx/quarkus-bom-3.2.11.Final-redhat-00001.json",
        "spdx/quarkus-bom-3.2.12.Final-redhat-00002.json",
    ])
    .await?;

    let service = AnalysisService::new(AnalysisConfig::default(), ctx.db.clone());

    // When: retrieve the first page twice with identical pagination
    let page1_first = service
        .retrieve(
            /* whatever query/filter arguments you use in test_pagination_offset, e.g.: */
            /* &some_query, */
            Paginated {
                offset: 0,
                limit: 2,
            },
            /* and any additional params such as sort/filter, if present */
        )
        .await?;

    let page1_second = service
        .retrieve(
            /* same arguments as above */
            /* &some_query, */
            Paginated {
                offset: 0,
                limit: 2,
            },
        )
        .await?;

    // Then: both calls should return the same total and the same items in the same order
    assert_eq!(page1_first.total, page1_second.total);
    assert_eq!(page1_first.items.len(), page1_second.items.len());
    assert_eq!(page1_first.items, page1_second.items);

    Ok(())
}

You will need to:

  1. Copy the exact retrieve invocation used in test_pagination_offset (same query/filter arguments, additional parameters, etc.) for both page1_first and page1_second so that the only varying factor is the repeated call.
  2. If retrieve requires additional context (e.g., a query struct, AdvisoryQuery, ComponentQuery, or feature flags), construct those exactly as in test_pagination_offset to ensure the test exercises the same path.
  3. If items is not directly Eq (e.g., contains non-Eq types), instead compare the relevant deterministic fields (e.g., (sbom_id, node_id) for each item) in order:
    let first_keys: Vec<_> = page1_first.items.iter().map(|item| (item.sbom_id, item.node_id)).collect();
    let second_keys: Vec<_> = page1_second.items.iter().map(|item| (item.sbom_id, item.node_id)).collect();
    assert_eq!(first_keys, second_keys);
  4. Place this new test function in modules/analysis/src/service/test/mod.rs near test_pagination_offset to keep pagination-related tests grouped.

@rh-jfuller

Copy link
Copy Markdown
Contributor

/scale-test

@github-actions

Copy link
Copy Markdown

🛠️ Scale test has started! Follow the progress here: Workflow Run

@rh-jfuller

Copy link
Copy Markdown
Contributor

note: scale-tests use datasets that are prob tied to main (and will not work against release branch)

@rh-jfuller rh-jfuller self-requested a review July 2, 2026 11:27
…eout

Apply LIMIT/OFFSET at the graph node filtering stage so only the
requested page of nodes runs through expensive ancestor/descendant
hydration. Previously all matching nodes were fully hydrated then
sliced with paginate_array.

- Add count_matching_nodes, collect_graph_paged, run_graph_query_paged
- Use BTreeSet for sorted unique SBOM IDs and order_by_asc for
  deterministic pagination ordering
- Update retrieve, retrieve_single, retrieve_latest to use paged path
- Add pagination endpoint and service-level tests

Implements TC-4365

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Code
@mrrajan mrrajan force-pushed the TC-4365-release-0.4.z branch from 4b07204 to 8a56de8 Compare July 2, 2026 11:59
@rh-jfuller

Copy link
Copy Markdown
Contributor

I think the 0.4.z ship has sailed ... @mrrajan can this be applied to main branch ?

@mrrajan

mrrajan commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@rh-jfuller Raised a PR based on main branch #2446

@rh-jfuller

Copy link
Copy Markdown
Contributor

can this be closed then ?

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants