Skip to content

RHINENG-32308: sync template advisories from the message - #2373

Merged
MichaelMraka merged 3 commits into
RedHatInsights:masterfrom
MichaelMraka:pr1
Oct 30, 2026
Merged

MichaelMraka merged 3 commits into
RedHatInsights:masterfrom
MichaelMraka:pr1

Conversation

@MichaelMraka

@MichaelMraka MichaelMraka commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

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

Enable configurable template advisory synchronization from template event messages while retaining the existing Content Sources API fallback.

New Features:

  • Support syncing template advisories directly from advisory IDs included in template events when enabled.

Enhancements:

  • Preserve the existing Content Sources API sync path behind a feature flag and define create/update behavior for advisory snapshots.

Documentation:

  • Document template advisory synchronization modes, rollout guidance, and event processing behavior.

Tests:

  • Add coverage for message-based advisory synchronization, empty snapshots, and continued API-based synchronization.

@MichaelMraka
MichaelMraka requested a review from a team as a code owner October 27, 2026 13:08
@sourcery-ai

sourcery-ai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Template events now carry optional advisory IDs, and the listener can synchronize template advisories directly from those message snapshots when templates_with_advisories is enabled, while retaining the existing Content Sources API path otherwise. The implementation centralizes snapshot diffing, handles empty or omitted advisory lists as removals, adds integration and parsing tests, and documents rollout behavior.

File-Level Changes

Change Details Files
Add advisory IDs to template events and support syncing them directly from Kafka messages behind a feature flag.
  • Extend the template payload with optional advisory_ids and verify decoding for populated and omitted fields.
  • Read templates_with_advisories from POD_CONFIG and use message advisories for both create and update events when enabled.
  • Treat missing or empty advisory lists as an empty snapshot, diff against stored rows, and trigger existing evaluation behavior when applicable.
base/mqueue/template_event.go
listener/listener.go
listener/templates.go
listener/template_advisories.go
listener/template_test.go
listener/template_advisories_test.go
Preserve the existing Content Sources API synchronization path when message-based synchronization is disabled.
  • Keep template-created events from syncing advisories in legacy mode.
  • Continue fetching advisories from Content Sources for template-updated events when the client is configured.
  • Add coverage proving the feature flag selects the intended source and that message values are ignored when disabled.
listener/templates.go
listener/template_advisories_test.go
Document the rollout behavior and advisory synchronization flow for both modes.
  • Describe the feature flag, event-type behavior, API fallback, and empty-snapshot semantics.
  • Document the recommended rollout constraint that Content Sources must provide advisory IDs before enabling the flag.
docs/md/architecture.md
Update listener configuration artifacts.
  • Modify the listener environment configuration to reflect the new setting.
conf/listener.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

@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 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="listener/templates.go" line_range="156" />
<code_context>
-		ctx := identity.WithIdentity(base.Context,
+	switch {
+	case templatesWithAdvisories:
+		err = applyTemplateAdvisories(ctx, accountID, row.ID, template.UUID, template.OrgID, template.AdvisoryIDs)
+		if err != nil {
+			return errors.Wrap(err, "syncing template advisories from message")
</code_context>
<issue_to_address>
**Failed advisory sync stays stale**

When an advisory lookup or database write fails during a message-based sync, `TemplateUpdate` returns the error, but `TemplatesMessageHandler` only logs it and returns nil, so `MakeRetryingHandler` acknowledges the message and the stored advisory state remains stale or partial.

Propagate the per-template error from `TemplatesMessageHandler` so the retry handler can retry the message.

Also at `listener/templates.go:157-158`.
</issue_to_address>

### Comment 2
<location path="listener/template_advisories.go" line_range="32-39" />
<code_context>

+// applyTemplateAdvisories diffs advisory names against template_advisory and writes the change.
+// advisoryIDs is the full snapshot. Nil and empty both mean no advisories.
+func applyTemplateAdvisories(ctx context.Context, accountID int, templateID int64, templateUUID, orgID string,
+	advisoryIDs []string) error {
 	stored, err := lookUpTemplateAdvisories(database.DB(ctx), accountID, templateID)
 	if err != nil {
 		return errors.Wrap(err, "looking up stored template advisories")
 	}

-	toAdd, toRemove := diffTemplateAdvisories(current.AdvisoryIDs, stored)
+	toAdd, toRemove := diffTemplateAdvisories(advisoryIDs, stored)
 	if len(toAdd) == 0 && len(toRemove) == 0 {
 		utils.LogInfo("template_uuid", templateUUID, "template advisories unchanged")
</code_context>
<issue_to_address>
**Concurrent snapshots mix advisory rows**

When two handlers process different snapshots for the same template concurrently, `applyTemplateAdvisories` reads and diffs the stored rows before opening its transaction, so both handlers can apply additions based on the same old set and leave rows from both snapshots; callers see an advisory set matching neither snapshot.

Serialize updates per template and read and diff the stored rows within that serialized transaction.
</issue_to_address>

### Comment 3
<location path="listener/templates.go" line_range="156" />
<code_context>
-		ctx := identity.WithIdentity(base.Context,
+	switch {
+	case templatesWithAdvisories:
+		err = applyTemplateAdvisories(ctx, accountID, row.ID, template.UUID, template.OrgID, template.AdvisoryIDs)
+		if err != nil {
+			return errors.Wrap(err, "syncing template advisories from message")
</code_context>
<issue_to_address>
**Older snapshots overwrite newer advisories**

When an older template event is processed after a newer event, `TemplateUpdate` applies each message's `AdvisoryIDs` as the full snapshot without comparing event times or revisions, so `applyTemplateAdvisories` replaces the newer advisory set with stale rows.

Compare the event time or revision with the latest processed value and ignore stale snapshots.

Also at `listener/templates.go:157`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and when enabled, the message snapshot is authoritative: missing or empty advisory_ids can delete stored template-advisory rows and trigger reevaluation. The default is off, and the rows are bounded and can be restored by resyncing the correct snapshot, but reverting the code alone does not restore deleted rows.

Blocking findings: listener/templates.go:156, listener/template_advisories.go:39, listener/templates.go:156


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread listener/templates.go
ctx := identity.WithIdentity(base.Context,
switch {
case templatesWithAdvisories:
err = applyTemplateAdvisories(ctx, accountID, row.ID, template.UUID, template.OrgID, template.AdvisoryIDs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High · Failed advisory sync stays stale

When an advisory lookup or database write fails during a message-based sync, TemplateUpdate returns the error, but TemplatesMessageHandler only logs it and returns nil, so MakeRetryingHandler acknowledges the message and the stored advisory state remains stale or partial.

Propagate the per-template error from TemplatesMessageHandler so the retry handler can retry the message.

Also at listener/templates.go:157-158.

Prompt for AI agents
In `listener/templates.go` at line 156:

**Failed advisory sync stays stale**

When an advisory lookup or database write fails during a message-based sync, `TemplateUpdate` returns the error, but `TemplatesMessageHandler` only logs it and returns nil, so `MakeRetryingHandler` acknowledges the message and the stored advisory state remains stale or partial.

Propagate the per-template error from `TemplatesMessageHandler` so the retry handler can retry the message.

Also at `listener/templates.go:157-158`.

Comment on lines +32 to +39
func applyTemplateAdvisories(ctx context.Context, accountID int, templateID int64, templateUUID, orgID string,
advisoryIDs []string) error {
stored, err := lookUpTemplateAdvisories(database.DB(ctx), accountID, templateID)
if err != nil {
return errors.Wrap(err, "looking up stored template advisories")
}

toAdd, toRemove := diffTemplateAdvisories(current.AdvisoryIDs, stored)
toAdd, toRemove := diffTemplateAdvisories(advisoryIDs, stored)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High · Concurrent snapshots mix advisory rows

When two handlers process different snapshots for the same template concurrently, applyTemplateAdvisories reads and diffs the stored rows before opening its transaction, so both handlers can apply additions based on the same old set and leave rows from both snapshots; callers see an advisory set matching neither snapshot.

Serialize updates per template and read and diff the stored rows within that serialized transaction.

Prompt for AI agents
In `listener/template_advisories.go` at lines 32-39:

**Concurrent snapshots mix advisory rows**

When two handlers process different snapshots for the same template concurrently, `applyTemplateAdvisories` reads and diffs the stored rows before opening its transaction, so both handlers can apply additions based on the same old set and leave rows from both snapshots; callers see an advisory set matching neither snapshot.

Serialize updates per template and read and diff the stored rows within that serialized transaction.

Comment thread listener/templates.go
ctx := identity.WithIdentity(base.Context,
switch {
case templatesWithAdvisories:
err = applyTemplateAdvisories(ctx, accountID, row.ID, template.UUID, template.OrgID, template.AdvisoryIDs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High · Older snapshots overwrite newer advisories

When an older template event is processed after a newer event, TemplateUpdate applies each message's AdvisoryIDs as the full snapshot without comparing event times or revisions, so applyTemplateAdvisories replaces the newer advisory set with stale rows.

Compare the event time or revision with the latest processed value and ignore stale snapshots.

Also at listener/templates.go:157.

Prompt for AI agents
In `listener/templates.go` at line 156:

**Older snapshots overwrite newer advisories**

When an older template event is processed after a newer event, `TemplateUpdate` applies each message's `AdvisoryIDs` as the full snapshot without comparing event times or revisions, so `applyTemplateAdvisories` replaces the newer advisory set with stale rows.

Compare the event time or revision with the latest processed value and ignore stale snapshots.

Also at `listener/templates.go:157`.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.90%. Comparing base (2912769) to head (8009f19).

Files with missing lines Patch % Lines
listener/templates.go 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2373      +/-   ##
==========================================
+ Coverage   59.89%   59.90%   +0.01%     
==========================================
  Files         157      157              
  Lines       10119    10127       +8     
==========================================
+ Hits         6061     6067       +6     
- Misses       3431     3432       +1     
- Partials      627      628       +1     
Flag Coverage Δ
unittests 59.90% <83.33%> (+0.01%) ⬆️

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 self-assigned this Oct 28, 2026
@MichaelMraka
MichaelMraka merged commit 29c80a7 into RedHatInsights:master Oct 30, 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