Skip to content

feat: add aggregator service with Kafka consumer skeleton#2263

Merged
katarinazaprazna merged 1 commit into
RedHatInsights:masterfrom
katarinazaprazna:add-aggregator-service
Jul 8, 2026
Merged

feat: add aggregator service with Kafka consumer skeleton#2263
katarinazaprazna merged 1 commit into
RedHatInsights:masterfrom
katarinazaprazna:add-aggregator-service

Conversation

@katarinazaprazna

@katarinazaprazna katarinazaprazna commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a new aggregator component that subscribes to patchman.advisory.update topic
  • Handler is a stub (consumes messages but does not process them yet)
  • Notifications publisher is configured but disabled until notification responsibility is migrated from the evaluator
  • No DB writes, no user-visible impact

What's next

This is the foundation for the aggregator consumer, which will add message deserialization, batching by (rh_account_id, workspace_id), account_advisory table updates, and parity checks

Follow-up

  • Open an app-interface MR to set REPLICAS_AGGREGATOR=1 for stage so that we can test the aggregator there
  • Update docs/md/architecture.md with a full aggregator component description once the consumer logic is implemented

Test plan

  • go build ./... compiles
  • Aggregator starts in test container, connects to DB and Kafka
  • Health probes (/healthz, /readyz) respond correctly
  • check-dockercomposes.sh passes
  • Deploy to stage, verify pod starts and stays healthy

Secure Coding Practices Checklist GitHub Link

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

Introduce a new aggregator service that consumes advisory update Kafka messages and prepares for future advisory aggregation and notifications.

New Features:

  • Add an aggregator component that subscribes to the patchman.advisory.update Kafka topic and exposes health/metrics endpoints.
  • Add deployment and docker-compose configurations for running the aggregator service in production and development environments.
  • Wire the aggregator entrypoint into the main application and container image build, including ClowdApp and env configuration parameters.

Enhancements:

  • Extend documentation to describe the new advisory aggregation flow and its place in the overall system architecture.

Build:

  • Include the aggregator code in the Docker image build context and ensure dev/prod docker-compose setups stay in sync via the compose check script.

Documentation:

  • Update AGENTS documentation to reference the new aggregator component and its role in the advisory processing pipeline.

Summary by Sourcery

Introduce a new aggregator service that consumes advisory update Kafka messages and is wired into the existing application and deployment stack.

New Features:

  • Add an aggregator component that subscribes to the patchman.advisory.update Kafka topic and exposes health and metrics endpoints.
  • Add aggregator service definitions to ClowdApp and docker-compose (dev and prod) for running the component alongside existing services.

Enhancements:

  • Integrate the aggregator entrypoint into the main application binary and include its sources in the Docker image build context.
  • Extend architecture documentation to describe the new advisory aggregation flow and the aggregator component’s role.

Build:

  • Update the Dockerfile to include the aggregator module in the build image and keep docker-compose.prod.yml in sync with docker-compose.yml via the compose check script.

Documentation:

  • Update AGENTS documentation to reference the aggregator component and its place in the advisory processing pipeline.

New component that consumes patchman.advisory.update topic. Includes
deployment configs, health probes, and a stub handler for advisory
update events. Notifications are disabled until the evaluator handoff.
@sourcery-ai

sourcery-ai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces a new aggregator service wired into the existing app, with deployment/docker-compose/Dockerfile wiring, Kafka consumer skeleton, health/metrics server, and optional notifications publisher, but no business logic yet.

File-Level Changes

Change Details Files
Add aggregator service entrypoint and Kafka consumer skeleton with health/metrics server.
  • Introduce aggregator.RunAggregator wired from main.go with its own process mode.
  • Read pod configuration for consumer count and notification behavior from PodConfig.
  • Configure advisory update topic from core configuration and set up optional notifications publisher.
  • Start a Gin-based HTTP server exposing health and readiness probes plus metrics port.
  • Spawn one or more Kafka readers on the advisory update topic using a retrying handler stub that currently no-ops.
aggregator/aggregator.go
aggregator/events.go
aggregator/notifications.go
main.go
Wire aggregator into container image and runtime/development orchestration.
  • Include the new aggregator/ directory in the Dockerfile build context.
  • Add an aggregator service to docker-compose.yml (dev) with gorun env, ports, volumes, and dependencies.
  • Add an aggregator service to docker-compose.prod.yml (prod) with separate env files and secrets mounts.
  • Extend the docker-compose parity check script to ensure aggregator service configuration stays in sync between dev and prod.
Dockerfile
docker-compose.yml
docker-compose.prod.yml
scripts/check-dockercomposes.sh
Add ClowdApp deployment configuration and tunable parameters for aggregator.
  • Define an aggregator deployment in clowdapp.yaml with its own container command, env vars, and resource requests/limits.
  • Configure initContainer for DB readiness using existing database_admin check script.
  • Add Kafka-related and profiler-related environment variables, including advisory update and notifications topics.
  • Introduce aggregator-specific ClowdApp parameters such as replica count, log level, DB debug flag, and memory limits with safe defaults (replicas 0).
deploy/clowdapp.yaml
Update documentation to reference the new advisory aggregation component and flow.
  • Add aggregator/ as the source for advisory aggregation to the AGENTS.md component table.
  • Extend the architecture/flow section to show the new advisory update topic and aggregator’s role in recomputing aggregates and emitting notifications.
AGENTS.md
Add configuration stubs for aggregator environment files.
  • Introduce aggregator.env and aggregator_common.env config files (currently empty stubs) for service-specific environment configuration.
conf/aggregator.env
conf/aggregator_common.env

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

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.75%. Comparing base (cc54b11) to head (603d9db).

Files with missing lines Patch % Lines
aggregator/aggregator.go 0.00% 31 Missing ⚠️
aggregator/notifications.go 0.00% 3 Missing ⚠️
main.go 0.00% 3 Missing ⚠️
aggregator/events.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2263      +/-   ##
==========================================
- Coverage   59.03%   58.75%   -0.29%     
==========================================
  Files         144      147       +3     
  Lines        9194     9233      +39     
==========================================
- Hits         5428     5425       -3     
- Misses       3194     3236      +42     
  Partials      572      572              
Flag Coverage Δ
unittests 58.75% <0.00%> (-0.29%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 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.

@katarinazaprazna katarinazaprazna marked this pull request as ready for review July 8, 2026 13:52
@katarinazaprazna katarinazaprazna requested a review from a team as a code owner July 8, 2026 13:52

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

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:

  • The enableNotifications flag is read from PodConfig but never used to gate notification behavior, so either remove it for now or wire it into configureNotifications / publishing logic to avoid confusion about how notifications are toggled.
  • Consider validating consumer_count from PodConfig (e.g., enforcing a minimum of 1) so a misconfiguration doesn’t silently result in zero Kafka consumers being spawned.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `enableNotifications` flag is read from `PodConfig` but never used to gate notification behavior, so either remove it for now or wire it into `configureNotifications` / publishing logic to avoid confusion about how notifications are toggled.
- Consider validating `consumer_count` from `PodConfig` (e.g., enforcing a minimum of 1) so a misconfiguration doesn’t silently result in zero Kafka consumers being spawned.

## Individual Comments

### Comment 1
<location path="aggregator/aggregator.go" line_range="19-21" />
<code_context>
+	consumerCount       int
+)
+
+func readPodConfig() {
+	consumerCount = utils.PodConfig.GetInt("consumer_count", 1)
+	enableNotifications = utils.PodConfig.GetBool("instant_notifications", false)
+}
+
</code_context>
<issue_to_address>
**suggestion:** Consider validating `consumer_count` to avoid silently running with zero consumers

If `consumer_count` is set to 0 or negative, no readers will be spawned and the service will appear healthy while not consuming messages. Consider enforcing a minimum of 1 (e.g., clamp `<= 0` to 1 and log a warning) to guard against misconfiguration.

Suggested implementation:

```golang
import (
	"app/base"
	"app/base/core"
	"app/base/mqueue"
	"app/base/utils"
	"log"
	"sync"

	"github.com/gin-gonic/gin"
)

```

```golang
func readPodConfig() {
	consumerCount = utils.PodConfig.GetInt("consumer_count", 1)
	if consumerCount <= 0 {
		log.Printf("invalid consumer_count=%d, defaulting to 1", consumerCount)
		consumerCount = 1
	}

	enableNotifications = utils.PodConfig.GetBool("instant_notifications", false)
}

```
</issue_to_address>

### Comment 2
<location path="aggregator/notifications.go" line_range="10-12" />
<code_context>
+
+var notificationsPublisher mqueue.Writer
+
+func configureNotifications() {
+	if topic := utils.CoreCfg.NotificationsTopic; topic != "" {
+		notificationsPublisher = mqueue.NewKafkaWriterFromEnv(topic)
+	}
+}
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Align `instant_notifications` config with notifications writer initialization

`configureNotifications` still creates a writer whenever `NotificationsTopic` is set, ignoring `instant_notifications`. If that flag is intended to control whether notifications are produced, also check `enableNotifications` before initializing the writer; otherwise, remove the unused flag to avoid misleading config.

Suggested implementation:

```golang
func configureNotifications() {
	// Only initialize the notifications writer if notifications are enabled
	if !enableNotifications {
		return
	}

	if topic := utils.CoreCfg.NotificationsTopic; topic != "" {
		notificationsPublisher = mqueue.NewKafkaWriterFromEnv(topic)
	}
}

```

To fully align `instant_notifications` with writer initialization, ensure:
1. `enableNotifications` is derived from the `instant_notifications` config (or equivalent) in your configuration bootstrap code, e.g.:
   - `enableNotifications = utils.CoreCfg.InstantNotifications` (or similar).
2. Any code path that toggles `instant_notifications` updates `enableNotifications` before `configureNotifications()` is called.
3. If `enableNotifications` is not yet defined in this package, declare it (likely as a `bool` at package scope) and initialize it alongside other feature flags, respecting existing conventions in the aggregator package.
</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 thread aggregator/aggregator.go
Comment on lines +19 to +21
func readPodConfig() {
consumerCount = utils.PodConfig.GetInt("consumer_count", 1)
enableNotifications = utils.PodConfig.GetBool("instant_notifications", false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Consider validating consumer_count to avoid silently running with zero consumers

If consumer_count is set to 0 or negative, no readers will be spawned and the service will appear healthy while not consuming messages. Consider enforcing a minimum of 1 (e.g., clamp <= 0 to 1 and log a warning) to guard against misconfiguration.

Suggested implementation:

import (
	"app/base"
	"app/base/core"
	"app/base/mqueue"
	"app/base/utils"
	"log"
	"sync"

	"github.com/gin-gonic/gin"
)
func readPodConfig() {
	consumerCount = utils.PodConfig.GetInt("consumer_count", 1)
	if consumerCount <= 0 {
		log.Printf("invalid consumer_count=%d, defaulting to 1", consumerCount)
		consumerCount = 1
	}

	enableNotifications = utils.PodConfig.GetBool("instant_notifications", false)
}

Comment on lines +10 to +12
func configureNotifications() {
if topic := utils.CoreCfg.NotificationsTopic; topic != "" {
notificationsPublisher = mqueue.NewKafkaWriterFromEnv(topic)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Align instant_notifications config with notifications writer initialization

configureNotifications still creates a writer whenever NotificationsTopic is set, ignoring instant_notifications. If that flag is intended to control whether notifications are produced, also check enableNotifications before initializing the writer; otherwise, remove the unused flag to avoid misleading config.

Suggested implementation:

func configureNotifications() {
	// Only initialize the notifications writer if notifications are enabled
	if !enableNotifications {
		return
	}

	if topic := utils.CoreCfg.NotificationsTopic; topic != "" {
		notificationsPublisher = mqueue.NewKafkaWriterFromEnv(topic)
	}
}

To fully align instant_notifications with writer initialization, ensure:

  1. enableNotifications is derived from the instant_notifications config (or equivalent) in your configuration bootstrap code, e.g.:
    • enableNotifications = utils.CoreCfg.InstantNotifications (or similar).
  2. Any code path that toggles instant_notifications updates enableNotifications before configureNotifications() is called.
  3. If enableNotifications is not yet defined in this package, declare it (likely as a bool at package scope) and initialize it alongside other feature flags, respecting existing conventions in the aggregator package.

@MichaelMraka MichaelMraka self-assigned this Jul 8, 2026
@katarinazaprazna katarinazaprazna merged commit 10532db into RedHatInsights:master Jul 8, 2026
8 checks passed
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.

3 participants