Skip to content

refactor: fetch source_document alongside, and only what's required#2299

Open
ctron wants to merge 18 commits intoguacsec:mainfrom
ctron:feature/perf_fix_1
Open

refactor: fetch source_document alongside, and only what's required#2299
ctron wants to merge 18 commits intoguacsec:mainfrom
ctron:feature/perf_fix_1

Conversation

@ctron
Copy link
Copy Markdown
Contributor

@ctron ctron commented Mar 19, 2026

Instead of fully fetching all information of an SBOM and then just using the ID, this fetches the bare minimum. But then also fetches the source_document in the same step. Passing on to other parts which need it. This should prevent one N+1 select issues.

Summary by Sourcery

Optimize SBOM fetching to include only required data and source documents, introduce a more flexible package representation, and add a new v3 SBOM listing API while deprecating older behaviors.

New Features:

  • Add a v3 SBOM listing endpoint that returns lightweight SBOM package summaries and supports group filtering.
  • Introduce generic package mapping via the IntoPackage trait to support both detailed and summary package views in SBOM-related APIs.
  • Expose slow SQL logging configuration through the TRUSTD_SLOW_SQL_THRESHOLD environment variable and enable slow statement logging when set.

Bug Fixes:

  • Ensure SBOM deletion also removes the associated source document while returning a 204 status instead of the previous SBOM payload.

Enhancements:

  • Refine SBOM queries to fetch SBOM, node, and source document in one step and avoid unnecessary joins and N+1 queries.
  • Generalize SBOM package relation types and pagination helpers to work with multi-entity (SelectThree) queries.
  • Adjust SBOM summary and details models to rely on mandatory sbom_node data and pre-fetched source documents.
  • Add an index on package_relates_to_package to improve right-side relationship lookups and related queries.
  • Update OpenAPI and Utoipa specifications to reflect new operation IDs, response schemas, and deprecations for SBOM endpoints.
  • Provide a default implementation for PaginatedResults and add slow SQL logging configuration to the database connector.

Documentation:

  • Document the TRUSTD_SLOW_SQL_THRESHOLD environment variable for configuring slow SQL logging.

Tests:

  • Update and extend SBOM and CycloneDX/SPDX test suites to use the new generic package APIs, v3 endpoints, and changed delete behavior.

Chores:

  • Add a new migration to create an index on the package_relates_to_package right-side reference table and include it in the migrator.
  • Add a minimal auth schema test configuration file used for authentication-related setups.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Mar 19, 2026

Reviewer's Guide

Refactors SBOM fetching and related APIs to eagerly load SBOM+node+source_document via SelectThree, introduces a generic IntoPackage abstraction and v3 SBOM listing that returns lightweight package summaries, updates delete/related/package endpoints and OpenAPI/utoipa metadata accordingly, and adds DB/util/migration support (SelectThree limiting/filtering, new p2p index, slow-SQL logging, and auth schema test).

Sequence diagram for updated SBOM delete endpoint behavior

sequenceDiagram
    actor User
    participant SbomEndpoints_delete as SbomEndpoints_delete
    participant Authorizer
    participant Database as Db
    participant SbomService
    participant Storage

    User->>SbomEndpoints_delete: DELETE /api/v2/sbom/{id}
    SbomEndpoints_delete->>Authorizer: require(user, ReadSbom)
    Authorizer-->>SbomEndpoints_delete: ok

    SbomEndpoints_delete->>Db: begin()
    Db-->>SbomEndpoints_delete: tx

    SbomEndpoints_delete->>SbomService: fetch_sbom(id, &tx)
    SbomService->>Db: SELECT sbom + sbom_node + source_document
    Db-->>SbomService: Option<(sbom, sbom_node, source_document)>
    SbomService-->>SbomEndpoints_delete: Some((sbom, sbom_node, source_document)) or None

    alt SBOM not found
        SbomEndpoints_delete-->>User: 404 Not Found
    else SBOM found
        SbomEndpoints_delete->>SbomService: delete_sbom(sbom.sbom_id, &tx)
        SbomService->>Db: DELETE sbom and related rows
        Db-->>SbomService: deleted:true/false
        SbomService-->>SbomEndpoints_delete: true or false

        alt delete returned false
            SbomEndpoints_delete-->>User: 404 Not Found
        else delete returned true
            SbomEndpoints_delete->>Db: tx.commit()
            Db-->>SbomEndpoints_delete: committed

            SbomEndpoints_delete->>Storage: delete_doc(SourceDocument::from_entity(&source_document))
            Storage-->>SbomEndpoints_delete: Ok or Err(logged and ignored)

            SbomEndpoints_delete-->>User: 204 No Content
        end
    end
Loading

ER diagram for SBOM, node, source_document, and package relationships

erDiagram
    SBOM {
        uuid sbom_id
        uuid document_id
        string document_namespace
        datetime published
        jsonb authors
        jsonb suppliers
        jsonb data_licenses
        uuid source_document_id
    }

    SBOM_NODE {
        uuid node_id
        uuid sbom_id
        string name
    }

    SOURCE_DOCUMENT {
        uuid id
        string content_type
        string location
    }

    SBOM_PACKAGE {
        string id
        uuid sbom_id
        string name
        string group
        string version
    }

    PACKAGE_RELATES_TO_PACKAGE {
        uuid sbom_id
        string relationship
        uuid left_node_id
        uuid right_node_id
        index idx_p2p_right_id "(sbom_id, relationship, right_node_id)"
    }

    SBOM ||--o{ SBOM_NODE : has_node
    SBOM ||--o{ SBOM_PACKAGE : has_package
    SBOM ||--o{ PACKAGE_RELATES_TO_PACKAGE : has_relation
    SOURCE_DOCUMENT ||--|| SBOM : is_source_of

    SBOM_NODE ||--|| SBOM_PACKAGE : describes

    PACKAGE_RELATES_TO_PACKAGE }o--|| SBOM_NODE : left_node
    PACKAGE_RELATES_TO_PACKAGE }o--|| SBOM_NODE : right_node
Loading

Class diagram for SBOM package abstractions and IntoPackage refactor

classDiagram
    direction LR

    class SbomService {
        +fetch_sbom~C:ConnectionTrait~(id:Id, connection:C) Result~Option~Tuple~sbom::Model,sbom_node::Model,source_document::Model~~~~, Error~
        +fetch_sboms~C:ConnectionTrait, P:IntoPackage~(search:Query, paginated:Paginated, options:FetchOptions, connection:C) Result~PaginatedResults~SbomSummary~P~~~~, Error~
        +describes_packages~C:ConnectionTrait, R:Resulting, P:IntoPackage~(sbom_id:Uuid, paginated:R, db:C) Result~R::Output~P~, Error~
        +fetch_related_packages~C:ConnectionTrait, R:Resulting, P:IntoPackage~(sbom_id:Uuid, search:Query, options:R, reference:SbomNodeReference, relationship:Option~Relationship~, db:C) Result~R::Output~SbomPackageRelation~P~~~~, Error~
    }

    class IntoPackage {
        <<trait>>
        +type Row
        +build_query(query:Select~package_relates_to_package::Entity~) Select~package_relates_to_package::Entity~
        +from_row(row:Row) Self
    }

    class SbomPackageSummary {
        +id:String
        +name:String
        +group:Option~String~
        +version:Option~String~
    }

    class SbomPackage {
        +id:String
        +name:String
        +group:Option~String~
        +version:Option~String~
        +purl:Vec~PurlSummary~
        +cpe:Vec~String~
        +licenses:Vec~LicenseInfo~
        +licenses_ref_mapping:Vec~LicenseRefMapping~
    }

    class SbomPackageRelation_P {
        <<generic P:IntoPackage>>
        +relationship:Relationship
        +package:P
    }

    class PackageCatcherBase {
        +id:String
        +name:String
        +group:Option~String~
        +version:Option~String~
    }

    class PackageCatcher {
        +base:PackageCatcherBase
        +purls:Vec~Value~
        +cpes:Value
        +licenses:Vec~LicenseBasicInfo~
    }

    class LicenseBasicInfo {
        +id:String
        +name:String
        +license_type:i32
    }

    class PaginatedResults_T {
        <<generic T>>
        +items:Vec~T~
        +total:u64
        +new~C,S1,S2~(db:C, fetch_selector:S1, count_selector:S2) PaginatedResults~R~
        +default() PaginatedResults~T~
    }

    IntoPackage <|.. SbomPackageSummary
    IntoPackage <|.. SbomPackage

    SbomPackageSummary ..> PackageCatcherBase : Row = PackageCatcherBase
    SbomPackage ..> PackageCatcher : Row = PackageCatcher

    SbomService --> IntoPackage : generic P
    SbomService --> SbomPackage
    SbomService --> SbomPackageSummary
    SbomService --> SbomPackageRelation_P

    SbomPackageRelation_P --> IntoPackage : P

    PackageCatcher --> PackageCatcherBase : base
    PackageCatcher --> LicenseBasicInfo

    PaginatedResults_T o--> "*" T
Loading

File-Level Changes

Change Details Files
Fetch SBOM with its node and source document in a single query and propagate this tuple through summaries and details.
  • Make SbomService::fetch_sbom public and change its return type to (sbom, sbom_node, source_document) using SelectThree with find_also_linked/find_also_related
  • Update fetch_sbom_summary, fetch_sboms, and other consumers to pass through the full tuple instead of just sbom or (sbom, Option)
  • Change SbomHead::from_entity and SbomSummary::from_entity to require a non-optional sbom_node and accept the already-fetched source_document, removing the extra source_document lookup
  • Adjust SbomDetails::from_entity and vulnerability/purl license models to use the new SbomHead signature and non-optional sbom_node
modules/fundamental/src/sbom/service/sbom.rs
modules/fundamental/src/sbom/model/mod.rs
modules/fundamental/src/sbom/model/details.rs
modules/fundamental/src/vulnerability/model/details/vulnerability_advisory.rs
modules/fundamental/src/purl/model/details/purl.rs
Generalize package-related queries with an IntoPackage abstraction and update related endpoints/types.
  • Introduce IntoPackage trait with build_query/from_row and implement it for SbomPackage and new SbomPackageSummary, splitting PackageCatcher into PackageCatcherBase+PackageCatcher
  • Refactor describes_packages and fetch_related_packages to be generic over P: IntoPackage and to build queries via P::build_query and map rows via P::from_row
  • Update SbomPackageRelation to be generic over P: IntoPackage and adjust callers, response typing, and deduplication logic accordingly
  • Remove package_from_row helper and wire new generics through tests and performance benchmarks
modules/fundamental/src/sbom/service/sbom.rs
modules/fundamental/src/sbom/model/mod.rs
modules/fundamental/src/sbom/model/details.rs
modules/fundamental/src/sbom/service/test.rs
modules/fundamental/tests/sbom/**/*.rs
Add v3 SBOM list endpoint returning package summaries and deprecate v2 list; align OpenAPI/utoipa schemas.
  • Split list SBOM endpoint into v2 and v3 modules; mark v2 as deprecated, change its operationId to v2/listSboms, and keep returning PaginatedResults
  • Add v3 /api/v3/sbom endpoint wired to SbomService::fetch_sboms::<, SbomPackageSummary> and returning PaginatedResults
  • Update OpenAPI paths/components for v2/v3 list endpoints, introduce PaginatedResults_SbomPackageSummary, and rename/retarget PaginatedResults_SbomPackageRelation* and SbomPackageRelation schemas to use the generic forms
  • Register both v2::all and v3::all handlers in the sbom endpoints router
modules/fundamental/src/sbom/endpoints/mod.rs
modules/fundamental/src/sbom/model/mod.rs
openapi.yaml
Change SBOM delete and related/package endpoints to use canonical sbom_id and new fetch tuple, and adjust status codes.
  • Update delete endpoint to use fetch_sbom to get (sbom, node, source_document), delete by v.sbom_id, delete the underlying source document using the eagerly loaded entity, and return HTTP 204 No Content instead of 200 with body
  • Change packages and related endpoints to resolve the SBOM via fetch_sbom, use sbom.sbom_id when calling fetch_sbom_packages/fetch_related_packages, and update utoipa response types to the generic PaginatedResults<SbomPackageRelation>
  • Update tests to assert 204 on delete and remove expectations about a returned SBOM body
modules/fundamental/src/sbom/endpoints/mod.rs
modules/fundamental/src/sbom/endpoints/test.rs
openapi.yaml
Extend shared DB utilities to support SelectThree limiting and filtering and multi-entity ID filtering.
  • Implement LimiterTrait for SelectThree to support paginated limiting/fetching over triple-entity queries
  • Extend Filtering trait to handle SelectThree by combining Columns for three entities
  • Implement TrySelectForId for SelectTwo and SelectThree so ID-based filtering can be applied to joined queries used by SbomService::fetch_sbom and similar
common/src/db/limiter.rs
common/src/db/query/filtering.rs
common/src/id.rs
Add performance/observability and schema/migration support.
  • Add optional slow SQL logging configuration controlled via TRUSTD_SLOW_SQL_THRESHOLD, enabling sqlx slow-statement logging when set
  • Provide Default for PaginatedResults to simplify tests and generic use
  • Add migration m0002130_p2p_right_index to create an index on package_relates_to_package (sbom_id, relationship, right_node_id)
  • Introduce a minimal auth schema test YAML for exercising authentication config parsing
common/src/db/mod.rs
common/src/model.rs
migration/src/m0002130_p2p_right_index.rs
migration/src/lib.rs
docs/env-vars.md
common/auth/schema/test.yaml

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

@ctron ctron requested a review from JimFuller-RedHat March 19, 2026 11:55
@ctron ctron force-pushed the feature/perf_fix_1 branch from a19e319 to 3d95abf Compare March 19, 2026 11:56
@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented Mar 19, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@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 found 1 issue, and left some high level feedback:

  • The new .filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?))) in the listing queries will now silently drop SBOMs that are missing a node or source document; if this is not intentional, consider either making the join inner or handling the None cases explicitly (e.g., erroring or providing a fallback) instead of filtering them out.
  • The tuple normalization from (sbom, Option<sbom_node>, Option<source_document>) to (sbom, sbom_node, source_document) is duplicated in several places; consider extracting a small helper (e.g., fn require_related(...) -> Option<...>) to centralize this logic and make the intent and failure mode clearer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `.filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?)))` in the listing queries will now silently drop SBOMs that are missing a node or source document; if this is not intentional, consider either making the join inner or handling the `None` cases explicitly (e.g., erroring or providing a fallback) instead of filtering them out.
- The tuple normalization from `(sbom, Option<sbom_node>, Option<source_document>)` to `(sbom, sbom_node, source_document)` is duplicated in several places; consider extracting a small helper (e.g., `fn require_related(...) -> Option<...>`) to centralize this logic and make the intent and failure mode clearer.

## Individual Comments

### Comment 1
<location path="modules/fundamental/src/sbom/model/mod.rs" line_range="88-95" />
<code_context>
     #[instrument(skip(service, tx), err(level=tracing::Level::INFO))]
     pub async fn from_entity<C>(
-        (sbom, node): (sbom::Model, Option<sbom_node::Model>),
+        (sbom, node, source_document): (sbom::Model, sbom_node::Model, source_document::Model),
         service: &SbomService,
         tx: &C,
</code_context>
<issue_to_address>
**question (bug_risk):** `SbomSummary::from_entity` now assumes node and source document always exist; consider whether failing earlier with an explicit error would make missing data easier to diagnose.

Previously, missing data either returned `Ok(None)` (no node) or an explicit `Error::NotFound` (no source document). Now this function assumes both `node` and `source_document` exist and depends on earlier filtering to enforce that.

If every SBOM must have both, consider keeping this function defensive by asserting that invariant or returning a clear error when it’s violated, so join/data issues are easier to trace. If the current behavior is intentional, it’d help to document that this function only accepts fully joined rows to avoid accidental use with partial data in the future.
</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 +88 to +95
(sbom, node, source_document): (sbom::Model, sbom_node::Model, source_document::Model),
service: &SbomService,
db: &C,
) -> Result<Option<SbomSummary>, Error> {
) -> Result<SbomSummary, Error> {
// TODO: consider improving the n-select issues here
let described_by = service.describes_packages(sbom.sbom_id, (), db).await?;

let source_document = sbom
.find_related(source_document::Entity)
.one(db)
.await?
.ok_or_else(|| Error::NotFound("Missing source document".to_string()))?;

Ok(match node {
Some(_) => Some(SbomSummary {
head: SbomHead::from_entity(&sbom, node, db).await?,
source_document: SourceDocument::from_entity(&source_document),
described_by,
}),
None => None,
Ok(SbomSummary {
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.

question (bug_risk): SbomSummary::from_entity now assumes node and source document always exist; consider whether failing earlier with an explicit error would make missing data easier to diagnose.

Previously, missing data either returned Ok(None) (no node) or an explicit Error::NotFound (no source document). Now this function assumes both node and source_document exist and depends on earlier filtering to enforce that.

If every SBOM must have both, consider keeping this function defensive by asserting that invariant or returning a clear error when it’s violated, so join/data issues are easier to trace. If the current behavior is intentional, it’d help to document that this function only accepts fully joined rows to avoid accidental use with partial data in the future.

Copy link
Copy Markdown
Contributor

@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 found 1 issue, and left some high level feedback:

  • The new fetch_sbom and list queries silently drop SBOMs when the node or source document is missing (node? / source_document? in the tuple and filter_map), which can hide data integrity issues; consider either surfacing an explicit error or returning a 404 instead of skipping these records.
  • The repeated pattern filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?))) is a bit opaque and duplicated; a small helper to enforce sbom_node/source_document presence (with a clear error path) would make the intent clearer and reduce repetition.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `fetch_sbom` and list queries silently drop SBOMs when the node or source document is missing (`node?` / `source_document?` in the tuple and `filter_map`), which can hide data integrity issues; consider either surfacing an explicit error or returning a 404 instead of skipping these records.
- The repeated pattern `filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?)))` is a bit opaque and duplicated; a small helper to enforce `sbom_node`/`source_document` presence (with a clear error path) would make the intent clearer and reduce repetition.

## Individual Comments

### Comment 1
<location path="modules/fundamental/src/sbom/endpoints/mod.rs" line_range="334" />
<code_context>
     ),
     responses(
-        (status = 200, description = "Matching SBOM", body = SbomSummary),
+        (status = 204, description = "Matching SBOM as deleted"),
         (status = 404, description = "The SBOM could not be found"),
     ),
</code_context>
<issue_to_address>
**nitpick:** OpenAPI description suggests a body for a 204 response, which cannot have one.

The handler returns `HttpResponse::NoContent().finish()`, so the 204 has no body. The description "Matching SBOM as deleted" can be read as if a body is returned. Please rephrase to make the empty response explicit (e.g. "SBOM deleted" or "Deletion successful; no content returned") to keep generated API docs/clients unambiguous.
</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.

),
responses(
(status = 200, description = "Matching SBOM", body = SbomSummary),
(status = 204, description = "Matching SBOM as deleted"),
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.

nitpick: OpenAPI description suggests a body for a 204 response, which cannot have one.

The handler returns HttpResponse::NoContent().finish(), so the 204 has no body. The description "Matching SBOM as deleted" can be read as if a body is returned. Please rephrase to make the empty response explicit (e.g. "SBOM deleted" or "Deletion successful; no content returned") to keep generated API docs/clients unambiguous.

@ctron ctron force-pushed the feature/perf_fix_1 branch from aad43fc to 1d977c0 Compare March 19, 2026 16:39
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 69.74170% with 82 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.47%. Comparing base (e116e98) to head (19b457f).

Files with missing lines Patch % Lines
modules/fundamental/src/sbom/endpoints/mod.rs 45.09% 21 Missing and 7 partials ⚠️
modules/fundamental/src/sbom/service/sbom.rs 76.27% 21 Missing and 7 partials ⚠️
common/src/db/query/filtering.rs 0.00% 7 Missing ⚠️
common/src/model.rs 0.00% 6 Missing ⚠️
...nerability/model/details/vulnerability_advisory.rs 86.11% 1 Missing and 4 partials ⚠️
common/src/id.rs 33.33% 3 Missing and 1 partial ⚠️
modules/fundamental/src/purl/model/details/purl.rs 0.00% 3 Missing ⚠️
common/src/db/mod.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2299      +/-   ##
==========================================
- Coverage   67.71%   67.47%   -0.24%     
==========================================
  Files         433      435       +2     
  Lines       24875    24692     -183     
  Branches    24875    24692     -183     
==========================================
- Hits        16843    16660     -183     
- Misses       7111     7139      +28     
+ Partials      921      893      -28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ctron ctron force-pushed the feature/perf_fix_1 branch from aff35fc to 436df3c Compare March 23, 2026 09:44
@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented Mar 23, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@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 found 3 issues, and left some high level feedback:

  • Both fetch_sbom and fetch_sboms now silently drop SBOMs where the sbom_node or source_document is None (via node? / source_document? in filter_map), which changes previous behavior and can hide data inconsistencies; consider either surfacing a specific error or at least logging when such rows are discarded.
  • The new v3 /api/v3/sbom endpoint is declared in OpenAPI (and utoipa::path) as returning PaginatedResults<SbomPackageSummary>, but the implementation still uses fetch_sboms which returns PaginatedResults<SbomSummary<P>>; align the schema or the implementation so the wire format and OpenAPI contract match.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Both `fetch_sbom` and `fetch_sboms` now silently drop SBOMs where the `sbom_node` or `source_document` is `None` (via `node?` / `source_document?` in `filter_map`), which changes previous behavior and can hide data inconsistencies; consider either surfacing a specific error or at least logging when such rows are discarded.
- The new v3 `/api/v3/sbom` endpoint is declared in OpenAPI (and `utoipa::path`) as returning `PaginatedResults<SbomPackageSummary>`, but the implementation still uses `fetch_sboms` which returns `PaginatedResults<SbomSummary<P>>`; align the schema or the implementation so the wire format and OpenAPI contract match.

## Individual Comments

### Comment 1
<location path="modules/fundamental/src/sbom/service/sbom.rs" line_range="82-84" />
<code_context>
-            .find_also_linked(SbomNodeLink)
-            .one(connection)
-            .await?)
+        let map = |(sbom, node, source_document)| Some((sbom, node?, source_document?));
+
+        Ok(select.one(connection).await?.and_then(map))
     }

</code_context>
<issue_to_address>
**issue (bug_risk):** `fetch_sbom` now silently drops SBOMs missing a node or source document instead of failing on missing source documents

Previously, a missing `source_document` caused `SbomSummary::from_entity` to return `Error::NotFound(..)`, while a missing `sbom_node` yielded `Ok(None)` and was filtered out. With the new mapping, both `sbom_node` and `source_document` are required, but either being missing now returns `None`, making this indistinguishable from a non‑existent SBOM id to the caller. If missing `source_document` should still be treated as a data integrity error, consider preserving the error behavior for that case (and only treating missing `sbom_node` as absence), or at least log when a row is dropped due to a missing join.
</issue_to_address>

### Comment 2
<location path="modules/fundamental/src/sbom/service/sbom.rs" line_range="300-307" />
<code_context>
-            .try_filter_map(futures_util::future::ok)
-            .try_collect()
-            .await?;
+        let items = stream::iter(
+            sboms
+                .into_iter()
+                .filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?))),
+        )
+        .then(|row| async { SbomSummary::from_entity(row, self, connection).await })
+        .try_collect()
+        .instrument(info_span!("from_entity"))
+        .await?;

</code_context>
<issue_to_address>
**issue (bug_risk):** `fetch_sboms` now also silently drops SBOM rows with missing node or source document, changing error semantics

The new `filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?)))` now skips any row missing `sbom_node` or `source_document`. Previously, only missing nodes were skipped; missing `source_document` caused `SbomSummary::from_entity` to error. This changes external behaviour (fewer results instead of an error) and can hide data issues. If this change is intentional, consider at least logging skipped rows or still erroring on missing `source_document` so data problems aren’t silently masked.
</issue_to_address>

### Comment 3
<location path="modules/fundamental/src/sbom/model/mod.rs" line_range="80-81" />
<code_context>
 }

 #[derive(Serialize, Deserialize, Debug, Clone, ToSchema)]
-pub struct SbomSummary {
+pub struct SbomSummary<P: IntoPackage = SbomPackage> {
     #[serde(flatten)]
     pub head: SbomHead,
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `ToSchema` on a generic `SbomSummary<P>` may be fragile for OpenAPI generation

Deriving `ToSchema` on a generic `SbomSummary<P: IntoPackage = SbomPackage>` requires utoipa to describe a generic type, which may not be well-supported and can lead to compile errors or odd schemas, especially with the default type parameter. Since the API only exposes specific instantiations (e.g. with `SbomPackage` or `SbomPackageSummary`), consider either introducing concrete wrapper types for those cases or removing `ToSchema` from the generic and deriving it only on the concrete types/aliases used in the OpenAPI spec.
</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 +82 to +84
let map = |(sbom, node, source_document)| Some((sbom, node?, source_document?));

Ok(select.one(connection).await?.and_then(map))
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.

issue (bug_risk): fetch_sbom now silently drops SBOMs missing a node or source document instead of failing on missing source documents

Previously, a missing source_document caused SbomSummary::from_entity to return Error::NotFound(..), while a missing sbom_node yielded Ok(None) and was filtered out. With the new mapping, both sbom_node and source_document are required, but either being missing now returns None, making this indistinguishable from a non‑existent SBOM id to the caller. If missing source_document should still be treated as a data integrity error, consider preserving the error behavior for that case (and only treating missing sbom_node as absence), or at least log when a row is dropped due to a missing join.

Comment on lines +300 to +307
let items = stream::iter(
sboms
.into_iter()
.filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?))),
)
.then(|row| async { SbomSummary::from_entity(row, self, connection).await })
.try_collect()
.instrument(info_span!("from_entity"))
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.

issue (bug_risk): fetch_sboms now also silently drops SBOM rows with missing node or source document, changing error semantics

The new filter_map(|(sbom, node, source_document)| Some((sbom, node?, source_document?))) now skips any row missing sbom_node or source_document. Previously, only missing nodes were skipped; missing source_document caused SbomSummary::from_entity to error. This changes external behaviour (fewer results instead of an error) and can hide data issues. If this change is intentional, consider at least logging skipped rows or still erroring on missing source_document so data problems aren’t silently masked.

@ctron ctron force-pushed the feature/perf_fix_1 branch 2 times, most recently from ac56fbb to 275994d Compare March 27, 2026 12:29
@ctron
Copy link
Copy Markdown
Contributor Author

ctron commented Mar 27, 2026

/scale-test

@github-actions
Copy link
Copy Markdown

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

ctron and others added 14 commits March 27, 2026 14:30
Instead of fully fetching all information of an SBOM and then just using
the ID, this fetches the bare minimum. But then also fetches the
source_document in the same step. Passing on to other parts which need
it. This should prevent one N+1 select issues.
This adds a v3 sbom list endpoint, which returns less information, but
is much faster.

Also, allow overriding the slow SQL threshold.
This was a red herring from AI. Node IDs are strings.
Co-authored-by: Claude <noreply@anthropic.com>
Fix license filter using UNION subquery instead of OR'd IN subqueries to
avoid full table scan on qualified_purl

Co-authored-by: Claude <noreply@anthropic.com>
Replaces OR'd IN subqueries with a single UNION subquery in fetch_sboms
and fetch_sbom_packages, avoiding full table scans on sbom and
sbom_package.

Co-authored-by: Claude <noreply@anthropic.com>
@ctron ctron force-pushed the feature/perf_fix_1 branch from 275994d to 19b457f Compare March 27, 2026 13:31
@github-actions
Copy link
Copy Markdown

Goose Report

Goose Attack Report

Plan Overview

Action Started Stopped Elapsed Users
Increasing 26-03-27 14:27:34 26-03-27 14:27:41 00:00:07 0 → 7
Maintaining 26-03-27 14:27:41 26-03-27 14:57:41 00:30:00 7
Decreasing 26-03-27 14:57:41 26-03-27 14:57:47 00:00:06 0 ← 7

Request Metrics

Method Name # Requests # Fails Average (ms) Min (ms) Max (ms) RPS Failures/s
GET download_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 338 (+278) 0 186.28 (-8.77) 25 (-19) 320 (-3) 0.19 (+0.15) 0.00 (+0.00)
GET get_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 338 (+278) 0 185.91 (+14.71) 30 (+7) 421 (+129) 0.19 (+0.15) 0.00 (+0.00)
GET get_advisory_by_doc_id 338 (+278) 0 17.21 (-9.37) 2 (-1) 77 (-42) 0.19 (+0.15) 0.00 (+0.00)
GET get_analysis_latest_cpe 343 (+283) 0 9.99 (-65.14) 1 (-3) 59 (-136) 0.19 (+0.16) 0.00 (+0.00)
GET get_analysis_status 343 (+283) 0 6.69 (-13.81) 1 (0) 49 (-34) 0.19 (+0.16) 0.00 (+0.00)
GET get_purl_details[0000054a-a69b-5…520-80752db183e6] 338 (+278) 0 205.50 (-71.85) 34 (-5) 653 (+37) 0.19 (+0.15) 0.00 (+0.00)
GET get_sbom[sha256:a3442b37…3040057f79c70669] 338 (+278) 0 186.48 (-8608.22) 28 (-256) 483 (-13733) 0.19 (+0.15) 0.00 (+0.00)
GET get_sbom_license_ids[urn:uuid:019c4a…8ba-3cc0260ef778] 338 (+278) 0 13.03 (-24.28) 1 (-1) 66 (-84) 0.19 (+0.15) 0.00 (+0.00)
GET list_advisory 338 (+278) 0 568.85 (+43.53) 151 (-9) 1452 (+453) 0.19 (+0.15) 0.00 (+0.00)
GET list_advisory_labels 343 (+283) 0 12675.59 (-744.14) 7791 (+4278) 23907 (+4929) 0.19 (+0.16) 0.00 (+0.00)
GET list_advisory_paginated 338 (+278) 0 555.49 (+91.86) 104 (-13) 1672 (+356) 0.19 (+0.15) 0.00 (+0.00)
GET list_importer 339 (+279) 0 8.09 (-6.88) 1 (0) 62 (-30) 0.19 (+0.16) 0.00 (+0.00)
GET list_organizations 338 (+278) 0 267.24 (+26.79) 40 (-9) 958 (+573) 0.19 (+0.15) 0.00 (+0.00)
GET list_packages 339 (+278) 0 584.99 (+109.79) 130 (+28) 1963 (+874) 0.19 (+0.15) 0.00 (+0.00)
GET list_packages_paginated 339 (+278) 0 537.27 (+136.04) 119 (+16) 1844 (+1078) 0.19 (+0.15) 0.00 (+0.00)
GET list_products 339 (+274) 0 16.09 (+3.48) 2 (-3) 157 (+105) 0.19 (+0.15) 0.00 (+0.00)
GET list_sboms 339 (+274) 0 1557.42 (-103585.53) 825 (-76389) 3500 (-128743) 0.19 (+0.15) 0.00 (+0.00)
GET list_sboms_paginated 343 (+283) 0 248.41 (-854.61) 96 (-548) 612 (-2421) 0.19 (+0.16) 0.00 (+0.00)
GET list_vulnerabilities 339 (+279) 0 506.04 (+33.11) 76 (-12) 1284 (+507) 0.19 (+0.16) 0.00 (+0.00)
GET list_vulnerabilities_paginated 339 (+279) 0 483.41 (+98.86) 80 (-3) 1059 (+277) 0.19 (+0.16) 0.00 (+0.00)
GET sbom_by_package[pkg:oci/web-ter…-bundle&tag=1.11] 338 (+278) 0 61.95 (-148.54) 9 (-56) 202 (-293) 0.19 (+0.15) 0.00 (+0.00)
GET search_advisory 339 (+279) 0 2916.14 (+1288.23) 163 (-28) 12248 (+7216) 0.19 (+0.16) 0.00 (+0.00)
GET search_exact_purl 339 (+274) 0 128.89 (-65.74) 16 (-135) 786 (+383) 0.19 (+0.15) 0.00 (+0.00)
GET search_licenses 34879 (+34810) 0 22.09 (-21.11) 2 (-4) 349 (+171) 19.38 (+19.34) 0.00 (+0.00)
GET search_purls 339 (+274) 0 2811.86 (-5914.86) 820 (-1075) 7322 (-8562) 0.19 (+0.15) 0.00 (+0.00)
GET search_purls_by_license 34878 (+34808) 0 7.22 (-25728.83) 1 (-5194) 101 (-299900) 19.38 (+19.34) 0.00 (-0.00)
GET search_sboms_by_license 34878 (+34809) 0 8.59 (-45.12) 1 (-5) 75 (-199) 19.38 (+19.34) 0.00 (+0.00)
POST get_recommendations[pkg:maven/io.ne…8.1.redhat-00033] 338 (+278) 0 1685.25 (+300.60) 225 (-378) 3353 (+459) 0.19 (+0.15) 0.00 (+0.00)
POST post_vulnerability_analyze[pkg:rpm/redhat/squid] 338 (+278) 0 6.48 (+0.98) 1 (0) 57 (+5) 0.19 (+0.15) 0.00 (+0.00)
Aggregated 113453 (+111663) 0 91.05 (-6078.63) 1 (0) 23907 (-276094) 63.03 (+62.03) 0.00 (-0.00)

Response Time Metrics

Method Name 50%ile (ms) 60%ile (ms) 70%ile (ms) 80%ile (ms) 90%ile (ms) 95%ile (ms) 99%ile (ms) 100%ile (ms)
GET download_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 190 (-10) 200 (-20) 210 (-20) 230 (-30) 260 (-20) 290 (-10) 300 (-20) 320 (0)
GET get_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 190 (0) 200 (0) 210 (0) 220 (-10) 260 (-10) 280 (+10) 340 (+50) 420 (+130)
GET get_advisory_by_doc_id 10 (-7) 14 (-8) 17 (-11) 23 (-25) 48 (-12) 54 (-15) 67 (-13) 77 (-42)
GET get_analysis_latest_cpe 6 (-72) 7 (-79) 9 (-87) 12 (-98) 24 (-116) 45 (-115) 57 (-113) 59 (-136)
GET get_analysis_status 3 (-9) 4 (-12) 5 (-14) 7 (-26) 14 (-39) 38 (-33) 44 (-34) 49 (-34)
GET get_purl_details[0000054a-a69b-5…520-80752db183e6] 200 (-90) 210 (-100) 230 (-110) 270 (-110) 300 (-110) 350 (-140) 430 (-70) 653 (+53)
GET get_sbom[sha256:a3442b37…3040057f79c70669] 190 (-10,810) 200 (-11,800) 210 (-11,790) 250 (-12,750) 290 (-12,710) 310 (-13,690) 360 (-13,640) 480 (-13,520)
GET get_sbom_license_ids[urn:uuid:019c4a…8ba-3cc0260ef778] 7 (-11) 9 (-19) 11 (-44) 16 (-48) 43 (-47) 50 (-50) 58 (-52) 66 (-84)
GET list_advisory 500 (0) 600 (0) 600 (0) 700 (+100) 800 (+100) 900 (+100) 1,000 (+100) 1,000 (+1)
GET list_advisory_labels 12,000 (-2,000) 13,000 (-1,000) 13,000 (-1,000) 14,000 (-1,000) 16,000 (-1,000) 21,000 (+3,000) 23,000 (+4,022) 23,907 (+4,929)
GET list_advisory_paginated 500 (+20) 600 (+100) 600 (+100) 700 (+100) 800 (+200) 1,000 (+300) 1,000 (0) 1,672 (+672)
GET list_importer 3 (-2) 4 (-2) 6 (-3) 8 (-9) 17 (-39) 44 (-19) 54 (-12) 62 (-30)
GET list_organizations 270 (0) 280 (0) 290 (+10) 310 (+20) 380 (+30) 420 (+50) 700 (+330) 958 (+573)
GET list_packages 600 (+110) 600 (+100) 600 (0) 700 (+100) 800 (+100) 1,000 (+200) 1,000 (0) 1,963 (+963)
GET list_packages_paginated 500 (+80) 500 (+20) 600 (+100) 600 (+100) 700 (+100) 900 (+300) 1,844 (+1,244) 1,844 (+1,078)
GET list_products 10 (-1) 12 (0) 15 (+2) 20 (+6) 48 (+27) 54 (+31) 61 (+29) 157 (+105)
GET list_sboms 1,000 (-104,000) 2,000 (-108,000) 2,000 (-118,000) 2,000 (-118,000) 2,000 (-130,000) 2,000 (-130,000) 3,000 (-129,000) 3,500 (-128,500)
GET list_sboms_paginated 230 (-770) 270 (-730) 290 (-710) 330 (-670) 380 (-620) 410 (-590) 500 (-2,500) 600 (-2,400)
GET list_vulnerabilities 500 (0) 500 (0) 600 (0) 600 (0) 700 (+100) 800 (+200) 1,000 (+300) 1,000 (+223)
GET list_vulnerabilities_paginated 480 (+60) 500 (+30) 500 (+20) 600 (+100) 600 (+100) 800 (+200) 1,000 (+300) 1,000 (+218)
GET sbom_by_package[pkg:oci/web-ter…-bundle&tag=1.11] 60 (-140) 69 (-151) 81 (-159) 94 (-166) 110 (-290) 130 (-290) 200 (-280) 200 (-295)
GET search_advisory 2,000 (+1,000) 3,000 (+1,000) 4,000 (+2,000) 5,000 (+2,000) 6,000 (+3,000) 7,000 (+4,000) 9,000 (+5,000) 12,000 (+7,000)
GET search_exact_purl 91 (-79) 100 (-70) 110 (-60) 170 (-40) 310 (+10) 320 (-70) 700 (+300) 786 (+386)
GET search_licenses 12 (-7) 15 (-21) 38 (-22) 46 (-32) 52 (-47) 55 (-75) 63 (-97) 349 (+171)
GET search_purls 3,000 (-6,000) 3,000 (-6,000) 3,000 (-6,000) 4,000 (-6,000) 4,000 (-9,000) 5,000 (-8,000) 7,000 (-7,000) 7,000 (-8,884)
GET search_purls_by_license 3 (-13,997) 4 (-14,996) 5 (-15,995) 7 (-16,993) 14 (-31,986) 44 (-45,956) 56 (-283,944) 100 (-299,900)
GET search_sboms_by_license 3 (-30) 4 (-36) 5 (-60) 8 (-86) 35 (-105) 45 (-125) 56 (-134) 75 (-195)
POST get_recommendations[pkg:maven/io.ne…8.1.redhat-00033] 2,000 (+1,000) 2,000 (+1,000) 2,000 (0) 2,000 (0) 2,000 (0) 2,000 (0) 3,000 (+1,000) 3,000 (+106)
POST post_vulnerability_analyze[pkg:rpm/redhat/squid] 3 (0) 4 (0) 4 (-2) 6 (-1) 14 (+3) 37 (+22) 47 (+30) 57 (+5)
Aggregated 5 (-265) 8 (-422) 12 (-588) 38 (-1,962) 51 (-12,949) 120 (-16,880) 2,000 (-118,000) 23,907 (-276,093)

Status Code Metrics

Method Name Status Codes
GET download_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 338 [200]
GET get_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 338 [200]
GET get_advisory_by_doc_id 338 [200]
GET get_analysis_latest_cpe 343 [200]
GET get_analysis_status 343 [200]
GET get_purl_details[0000054a-a69b-5…520-80752db183e6] 338 [200]
GET get_sbom[sha256:a3442b37…3040057f79c70669] 338 [200]
GET get_sbom_license_ids[urn:uuid:019c4a…8ba-3cc0260ef778] 338 [200]
GET list_advisory 338 [200]
GET list_advisory_labels 343 [200]
GET list_advisory_paginated 338 [200]
GET list_importer 339 [200]
GET list_organizations 338 [200]
GET list_packages 339 [200]
GET list_packages_paginated 339 [200]
GET list_products 339 [200]
GET list_sboms 339 [200]
GET list_sboms_paginated 343 [200]
GET list_vulnerabilities 339 [200]
GET list_vulnerabilities_paginated 339 [200]
GET sbom_by_package[pkg:oci/web-ter…-bundle&tag=1.11] 338 [200]
GET search_advisory 339 [200]
GET search_exact_purl 339 [200]
GET search_licenses 34,879 [200]
GET search_purls 339 [200]
GET search_purls_by_license 34,878 [200]
GET search_sboms_by_license 34,878 [200]
POST get_recommendations[pkg:maven/io.ne…8.1.redhat-00033] 338 [200]
POST post_vulnerability_analyze[pkg:rpm/redhat/squid] 338 [200]
Aggregated 113,453 [200]

Transaction Metrics

Transaction # Times Run # Fails Average (ms) Min (ms) Max (ms) RPS Failures/s
WebsiteUser
0.0 logon 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
0.1 website_index 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
0.2 website_openapi 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
0.3 website_sboms 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
0.4 website_packages 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
0.5 website_advisories 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
0.6 website_importers 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
RestAPIUser
1.0 logon 338 (+278) 0 (0) 13.66 (+0.96) 7 (+1) 38 (+15) 0.19 (+0.15) 0.00 (+0.00)
1.1 list_organizations 338 (+278) 0 (0) 267.38 (+26.81) 40 (-9) 958 (+573) 0.19 (+0.15) 0.00 (+0.00)
1.2 list_advisory 338 (+278) 0 (0) 568.91 (+43.49) 151 (-9) 1452 (+453) 0.19 (+0.15) 0.00 (+0.00)
1.3 list_advisory_paginated 338 (+278) 0 (0) 555.53 (+91.87) 104 (-13) 1672 (+356) 0.19 (+0.15) 0.00 (+0.00)
1.4 get_advisory_by_doc_id 338 (+278) 0 (0) 17.26 (-9.36) 2 (-1) 77 (-43) 0.19 (+0.15) 0.00 (+0.00)
1.5 search_advisory 339 (+279) 0 (0) 2916.18 (+1288.19) 163 (-28) 12248 (+7216) 0.19 (+0.16) 0.00 (+0.00)
1.6 list_vulnerabilities 339 (+279) 0 (0) 506.11 (+33.11) 76 (-13) 1284 (+507) 0.19 (+0.16) 0.00 (+0.00)
1.7 list_vulnerabilities_paginated 339 (+279) 0 (0) 483.45 (+98.83) 80 (-3) 1059 (+277) 0.19 (+0.16) 0.00 (+0.00)
1.8 list_importer 339 (+279) 0 (0) 8.13 (-6.86) 1 (0) 62 (-30) 0.19 (+0.16) 0.00 (+0.00)
1.9 list_packages 339 (+278) 0 (0) 585.05 (+109.79) 130 (+28) 1963 (+874) 0.19 (+0.15) 0.00 (+0.00)
1.10 list_packages_paginated 339 (+278) 0 (0) 537.32 (+136.01) 119 (+16) 1844 (+1078) 0.19 (+0.15) 0.00 (+0.00)
1.11 search_purls 339 (+274) 0 (0) 2811.92 (-5914.84) 820 (-1075) 7322 (-8562) 0.19 (+0.15) 0.00 (+0.00)
1.12 search_exact_purl 339 (+274) 0 (0) 128.95 (-65.73) 16 (-135) 786 (+383) 0.19 (+0.15) 0.00 (+0.00)
1.13 list_products 339 (+274) 0 (0) 16.15 (+3.47) 2 (-4) 157 (+105) 0.19 (+0.15) 0.00 (+0.00)
1.14 list_sboms 339 (+274) 0 (0) 1557.47 (-103585.55) 826 (-76388) 3500 (-128743) 0.19 (+0.15) 0.00 (+0.00)
1.15 list_sboms_paginated 343 (+283) 0 (0) 248.48 (-854.61) 96 (-548) 612 (-2421) 0.19 (+0.16) 0.00 (+0.00)
1.16 get_analysis_status 343 (+283) 0 (0) 6.72 (-13.85) 1 (0) 49 (-34) 0.19 (+0.16) 0.00 (+0.00)
1.17 get_analysis_latest_cpe 343 (+283) 0 (0) 10.05 (-65.15) 1 (-3) 61 (-134) 0.19 (+0.16) 0.00 (+0.00)
1.18 list_advisory_labels 343 (+283) 0 (0) 12675.64 (-744.19) 7791 (+4278) 23907 (+4929) 0.19 (+0.16) 0.00 (+0.00)
1.19 get_sbom[sha256:a3442b37…3040057f79c70669] 338 (+278) 0 (0) 186.52 (-8608.23) 29 (-255) 483 (-13733) 0.19 (+0.15) 0.00 (+0.00)
1.20 sbom_by_package[pkg:oci/web-ter…-bundle&tag=1.11] 338 (+278) 0 (0) 62.02 (-148.60) 9 (-56) 202 (-293) 0.19 (+0.15) 0.00 (+0.00)
1.21 get_sbom_license_ids[urn:uuid:019c4a…8ba-3cc0260ef778] 338 (+278) 0 (0) 13.08 (-24.34) 1 (-1) 66 (-84) 0.19 (+0.15) 0.00 (+0.00)
1.22 post_vulnerability_analyze[pkg:rpm/redhat/squid] 338 (+278) 0 (0) 6.54 (+0.99) 1 (0) 57 (+5) 0.19 (+0.15) 0.00 (+0.00)
1.23 get_purl_details[0000054a-a69b-5…520-80752db183e6] 338 (+278) 0 (0) 205.55 (-71.83) 34 (-5) 653 (+37) 0.19 (+0.15) 0.00 (+0.00)
1.24 get_recommendations[pkg:maven/io.ne…8.1.redhat-00033] 338 (+278) 0 (0) 1685.33 (+300.58) 225 (-378) 3353 (+458) 0.19 (+0.15) 0.00 (+0.00)
1.25 download_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 338 (+278) 0 (0) 186.37 (-8.76) 25 (-19) 320 (-3) 0.19 (+0.15) 0.00 (+0.00)
1.26 get_advisory[5b25cdef-428a-4…29e-fbb3415c22ab] 338 (+278) 0 (0) 185.98 (+14.69) 30 (+7) 421 (+129) 0.19 (+0.15) 0.00 (+0.00)
RestAPIUserSlow
2.0 logon 34878 (+34809) 0 (0) 11.45 (+0.54) 6 (-1) 55 (+29) 19.38 (+19.34) 0.00 (+0.00)
2.1 search_licenses 34879 (+34810) 0 (0) 22.32 (-21.01) 2 (-4) 349 (+167) 19.38 (+19.34) 0.00 (+0.00)
2.2 search_sboms_by_license 34878 (+34809) 0 (0) 8.62 (-45.19) 1 (-5) 75 (-199) 19.38 (+19.34) 0.00 (+0.00)
2.3 search_purls_by_license 34878 (+34808) 0 (0) 7.26 (-25728.87) 1 (-5194) 101 (-299900) 19.38 (+19.34) 0.00 (+0.00)
RestAPIUserDelete
3.0 logon 510 (+1) 0 (0) 10.98 (+0.28) 6 (0) 37 (-4) 0.28 (+0.00) 0.00 (+0.00)
RestAdvisoryLableUser
4.0 logon 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
Aggregated 149179 (+146751) 0 (0) 69.24 (-4479.25) 1 (0) 23907 (-276094) 82.88 (+81.53) 0.00 (+0.00)

Scenario Metrics

Transaction # Users # Times Run Average (ms) Min (ms) Max (ms) Scenarios/s Iterations
WebsiteUser 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
RestAPIUser 5 (0) 338 (+278) 26407.68 (-116506.77) 18503 (-83691) 43015 (-135799) 0.19 (+0.15) 67.60 (+55.60)
RestAPIUserSlow 1 (0) 34878 (+34809) 51.11 (-25970.35) 12 (-5220) 370 (-299796) 19.38 (+19.34) 34878.00 (+34809.00)
RestAPIUserDelete 1 (0) 510 (+1) 3534.64 (-7.61) 3023 (+7) 4029 (+11) 0.28 (+0.00) 510.00 (+1.00)
RestAdvisoryLableUser 0 (0) 0 (0) 0.00 (+0.00) 0 (0) 0 (0) 0.00 (+0.00) 0.00 (+0.00)
Aggregated 7 (0) 35726 (+35088) 350.20 (-18730.29) 12 (-3004) 43015 (-257151) 19.85 (+19.49) 35455.60 (+34865.60)

📄 Full Report (Go to "Artifacts" and download report)

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.

1 participant