Skip to content

chore(RHINENG-24557): Create a command that reproduces ephemeral performance issues#3689

Open
fstavela wants to merge 2 commits intomasterfrom
fstavela/api-performance-command
Open

chore(RHINENG-24557): Create a command that reproduces ephemeral performance issues#3689
fstavela wants to merge 2 commits intomasterfrom
fstavela/api-performance-command

Conversation

@fstavela
Copy link
Contributor

@fstavela fstavela commented Mar 4, 2026

Overview

This PR is being created to address RHINENG-24557.

Devprod team has asked me to give them a simple way to reproduce the performance issues that we see in ephemeral with Kessel Phase 1 enabled: RHCLOUD-45279

Example usage:

iqe host_inventory measure-api-get-times --user insights_inventory_qe -n 5

This can be used in any environment.

PR Checklist

  • Keep PR title short, ideally under 72 characters
  • Descriptive comments provided in complex code blocks
  • Include raw query examples in the PR description, if adding/modifying SQL query
  • Tests: validate optimal/expected output
  • Tests: validate exceptions and failure scenarios
  • Tests: edge cases
  • Recovers or fails gracefully during potential resource outages (e.g. DB, Kafka)
  • Uses type hinting, if convenient
  • Documentation, if this PR changes the way other services interact with host inventory
  • Links to related PRs

Secure Coding Practices Documentation Reference

You can find documentation on this checklist here.

Secure Coding Checklist

  • Input Validation
  • Output Encoding
  • Authentication and Password Management
  • Session Management
  • Access Control
  • Cryptographic Practices
  • Error Handling and Logging
  • Data Protection
  • Communication Security
  • System Configuration
  • Database Security
  • File Management
  • Memory Management
  • General Coding Practices

Summary by Sourcery

Add a CLI command to upload host archives and measure host inventory GET API performance.

New Features:

  • Introduce a measure_api_get_times host-inventory command to automate uploading test archives and benchmarking key GET endpoints.

Enhancements:

  • Collect and print request timing statistics for host inventory GET operations to aid in diagnosing ephemeral performance issues.

@fstavela fstavela requested a review from a team as a code owner March 4, 2026 15:04
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 4, 2026

Reviewer's Guide

Adds a new IQE host_inventory CLI command that uploads a batch of archives, exercises GET /host_exists and GET /hosts/<host_ids>, and prints timing statistics to help reproduce and measure ephemeral performance issues.

Sequence diagram for the new measure_api_get_times CLI command

sequenceDiagram
    actor User
    participant iqe_host_inventory_cli
    participant application
    participant application_host_inventory
    participant upload_api
    participant hosts_api
    participant stats_logger

    User->>iqe_host_inventory_cli: measure_api_get_times(number, user, display_name_prefix, base_archive, archive_repo, sleep_seconds, cache_refresh_wait_time)

    iqe_host_inventory_cli->>iqe_host_inventory_cli: generate_display_name(panic_prevention=display_name_prefix)
    iqe_host_inventory_cli->>iqe_host_inventory_cli: build_host_archive(...) for each host

    iqe_host_inventory_cli->>application: _app_with_maybe_user(primary_application, user)
    activate application
    application-->>iqe_host_inventory_cli: application context

    iqe_host_inventory_cli->>application_host_inventory: get host_inventory from application

    iqe_host_inventory_cli->>upload_api: async_upload_archives(archives)
    upload_api-->>iqe_host_inventory_cli: archives accepted

    iqe_host_inventory_cli->>iqe_host_inventory_cli: sleep(sleep_seconds)

    loop for each insights_id
        iqe_host_inventory_cli->>hosts_api: get_host_exists(insights_id)
        hosts_api-->>iqe_host_inventory_cli: host_exists_response(id)
        iqe_host_inventory_cli->>iqe_host_inventory_cli: append host_id
        alt cache_refresh_wait_time > 0
            iqe_host_inventory_cli->>iqe_host_inventory_cli: sleep(cache_refresh_wait_time)
        end
    end

    alt host_ids not empty
        iqe_host_inventory_cli->>hosts_api: get_hosts_by_id(host_ids)
        hosts_api-->>iqe_host_inventory_cli: hosts_list
    end

    iqe_host_inventory_cli->>stats_logger: log_request_statistics()
    stats_logger-->>iqe_host_inventory_cli: report

    iqe_host_inventory_cli->>User: print performance report
Loading

Updated class diagram for the measure_api_get_times command and related components

classDiagram

    class MeasureApiGetTimesCommand {
        +measure_api_get_times(obj, number, user, display_name_prefix, base_archive, archive_repo, sleep_seconds, cache_refresh_wait_time) void
    }

    class ApplicationContext {
        +primary_application
    }

    class ApplicationHostInventory {
        +upload UploadAPI
        +apis HostInventoryApis
    }

    class UploadAPI {
        +async_upload_archives(archives) void
    }

    class HostInventoryApis {
        +hosts HostsApi
        +log_request_statistics() string
    }

    class HostsApi {
        +get_host_exists(insights_id) HostExistsResponse
        +get_hosts_by_id(host_ids) HostsResponse
    }

    class HostExistsResponse {
        +id string
    }

    class Archive {
        +insights_id string
        +display_name string
    }

    class ArchiveBuilder {
        +build_host_archive(display_name, base_archive, archive_repo) Archive
        +generate_display_name(panic_prevention) string
    }

    MeasureApiGetTimesCommand --> ApplicationContext : uses
    MeasureApiGetTimesCommand --> ArchiveBuilder : uses
    MeasureApiGetTimesCommand --> Archive : creates
    MeasureApiGetTimesCommand --> ApplicationHostInventory : uses

    ApplicationContext --> ApplicationHostInventory : provides
    ApplicationHostInventory --> UploadAPI : has
    ApplicationHostInventory --> HostInventoryApis : has
    HostInventoryApis --> HostsApi : has

    UploadAPI --> Archive : uploads
    HostsApi --> HostExistsResponse : returns
Loading

File-Level Changes

Change Details Files
Introduce a new benchmark command to upload archives, call host APIs, and log request timing statistics.
  • Add measure_api_get_times click command with options for host count, user, display name prefix, archive source, sleep timings, and cache-refresh wait time
  • Build a configurable number of host archives using generate_display_name and build_host_archive helpers
  • Upload all generated archives asynchronously via host_inventory.upload.async_upload_archives and wait before querying
  • Iteratively call host_inventory.apis.hosts.get_host_exists for each insights_id, collecting host_ids and optionally sleeping between calls to simulate cache refresh behavior
  • Call host_inventory.apis.hosts.get_hosts_by_id once with all collected host_ids and then print aggregated request statistics from host_inventory.apis.log_request_statistics
iqe-host-inventory-plugin/iqe_host_inventory/grafted_commands.py

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

SC Environment Impact Assessment

Overall Impact:NONE

No SC Environment-specific impacts detected in this PR.

What was checked

This PR was automatically scanned for:

  • Database migrations
  • ClowdApp configuration changes
  • Kessel integration changes
  • AWS service integrations (S3, RDS, ElastiCache)
  • Kafka topic changes
  • Secrets management changes
  • External dependencies

Copy link
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 2 issues, and left some high level feedback:

  • In the loop calling get_host_exists, consider guarding against missing or unexpected responses (e.g., response without an id) so you don’t append invalid IDs to host_ids and potentially send bad data into get_hosts_by_id.
  • Sleeping cache_refresh_wait_time after every single get_host_exists can make the command very slow for higher -n values; if you only need to approximate cache-refresh behavior, consider an option to sleep less frequently (e.g., every N hosts) or once after a batch.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the loop calling `get_host_exists`, consider guarding against missing or unexpected responses (e.g., `response` without an `id`) so you don’t append invalid IDs to `host_ids` and potentially send bad data into `get_hosts_by_id`.
- Sleeping `cache_refresh_wait_time` after every single `get_host_exists` can make the command very slow for higher `-n` values; if you only need to approximate cache-refresh behavior, consider an option to sleep less frequently (e.g., every N hosts) or once after a batch.

## Individual Comments

### Comment 1
<location path="iqe-host-inventory-plugin/iqe_host_inventory/grafted_commands.py" line_range="314-315" />
<code_context>
+
+
+@host_inventory_group.command()
+@click.option("-n", "--number", help="Number of hosts to create", default=51)
+@click.option(
+    "--user",
+    help="C.R.C user from iqe-core or local config. (Default: insights_qa)",
+    default="insights_qa",
+)
+@click.option(
+    "--display-name-prefix", help="Display name prefix for hosts", default="rhiqe.example"
+)
+@click.option("--base-archive", help="Name of the base archive", default=None)
+@click.option("--archive-repo", help="IQE plugin with the archive", default=None)
+@click.option(
+    "--sleep",
+    "sleep_seconds",
+    help="Seconds to sleep after upload before querying",
+    default=30,
+    type=float,
+)
+@click.option(
+    "--cache-refresh-wait-time",
+    help="Seconds to sleep between API requests for cache refresh (0 to skip)",
+    default=20,
+    type=float,
+)
+@click.pass_obj
+def measure_api_get_times(
+    obj: Any,
+    number: int,
+    user: str,
+    display_name_prefix: str,
</code_context>
<issue_to_address>
**suggestion:** Consider validating that `number` is positive to avoid unnecessary work or confusing output when it is 0 or negative.

With `number <= 0`, this will build and upload an empty archive list and still run the rest of the flow, which is likely confusing. Consider either enforcing `number > 0` at the CLI level (e.g., `click.IntRange(min=1)` or an early validation check) or short-circuiting with a clear message and exiting before upload/benchmarking when `number <= 0`.

```suggestion
@host_inventory_group.command()
@click.option(
    "-n",
    "--number",
    help="Number of hosts to create (must be >= 1)",
    default=51,
    type=click.IntRange(min=1),
)
```
</issue_to_address>

### Comment 2
<location path="iqe-host-inventory-plugin/iqe_host_inventory/grafted_commands.py" line_range="385-391" />
<code_context>
+        insights_ids: list[str] = [archive.insights_id for archive in archives]
+        host_ids: list[str] = []
+
+        click.echo(f"Calling GET /host_exists for {len(insights_ids)} hosts...")
+        for insights_id in insights_ids:
+            response = host_inventory.apis.hosts.get_host_exists(insights_id=insights_id)
+            host_ids.append(response.id)
+            if cache_refresh_wait_time > 0:
+                click.echo(f"Sleeping {cache_refresh_wait_time}s for cache refresh...")
+                time.sleep(cache_refresh_wait_time)
+
+        click.echo(f"Found {len(host_ids)} / {len(insights_ids)} hosts via GET /host_exists.")
</code_context>
<issue_to_address>
**suggestion (performance):** The per-host sleep in the loop may make runs impractically long for larger `number` values.

Because the sleep is inside the loop, total runtime grows with `number * cache_refresh_wait_time` (plus request latency and `sleep_seconds`), so large `number` values can lead to multi-minute/hour runs. If the intent is to pause between batches rather than per host, consider sleeping once every N hosts, only before the first request, or making this configurable (e.g., batch size or a flag to disable per-request sleep while keeping an initial pause).

Suggested implementation:

```python
        # --- Benchmark GET /host_exists ---
        insights_ids: list[str] = [archive.insights_id for archive in archives]
        host_ids: list[str] = []

        if cache_refresh_wait_time > 0:
            click.echo(
                f"Sleeping {cache_refresh_wait_time}s before GET /host_exists calls for cache refresh..."
            )
            time.sleep(cache_refresh_wait_time)

```

```python
        click.echo(f"Calling GET /host_exists for {len(insights_ids)} hosts...")
        for insights_id in insights_ids:
            response = host_inventory.apis.hosts.get_host_exists(insights_id=insights_id)
            host_ids.append(response.id)

```
</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.

@fstavela fstavela force-pushed the fstavela/api-performance-command branch 2 times, most recently from 4d0fc70 to fa61172 Compare March 4, 2026 15:17
@jpramos123 jpramos123 force-pushed the fstavela/api-performance-command branch from fa61172 to 99d466e Compare March 4, 2026 18:56
@thearifismail thearifismail enabled auto-merge (squash) March 4, 2026 19:02
@thearifismail
Copy link
Contributor

/retest

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.

4 participants