Skip to content

Re-order listener creation logic to do concurrency protection in core - #1776

Merged
EnriqueL8 merged 2 commits into
mainfrom
parallel-listener-creation
Sep 1, 2026
Merged

Re-order listener creation logic to do concurrency protection in core#1776
EnriqueL8 merged 2 commits into
mainfrom
parallel-listener-creation

Conversation

@peterbroadhurst

@peterbroadhurst peterbroadhurst commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Problem

During creation of a listener, the order in FireFly core is currently:

  1. Generate the ID of the listener
  2. Create in backend connector using a name built from the ID
    • ff-sub-<namespace>-<listener_id> for example
  3. Create in FF DB with backendId already known

The problem with this, is that if two API calls are racing performing (1) & (2), only one can win in (3) and the other will fail with:

INFO <-- POST /api/v1/namespaces/NAMESPACE/contracts/listeners [500] (361.76ms): FF00177: Database insert failed: pq: duplicate key value violates unique constraint "contractsubscriptions_name"

This leaks a listener/subscription in the backend connection, because we generated a unique ID in (1).

Note the listener is inert, as it doesn't match a backendID of any listener in FireFly core, so the events are discarded with a line like:

WARN Event received from unknown subscription 436e4afe-931c-4c20-ba3b-7ea2ddc904fb

Fix

Proposal is to just change the order:

  1. Generate the ID of the listener
  2. Create in FF DB with a placeholder backend ID
  3. Create in backend connector using a name built from the ID
  4. Update the FF DB with the backend ID

If (2) fails we haven't started creating the backend connector listener/subscription - so no leak.

If in the tiny edge case of a termination of the runtime between (3) and (4), it's not a leak - because the creation logic runs on startup to reconcile the two. We do a query-by-ID, then a create. We find the listener exists in the backend, and store the ID.

The only behavior change, is that the default name of a listener if one is created without a name becomes the FireFly ID, not the initial backend ID. Either is confusing... I assert that the backend ID which can actually change over time, is the more confusing. So I propose this is inert to end users, and positive change.

Types of changes

  • Bug fix
  • New feature added
  • Documentation Update

Please make sure to follow these points

  • I have read the contributing guidelines.
  • I have performed a self-review of my own code or work.
  • I have commented my code, particularly in hard-to-understand areas.
  • My changes generates no new warnings.
  • I have added tests that prove my fix is effective or that my feature works.
  • My changes have sufficient code coverage (unit, integration, e2e tests).

Signed-off-by: Peter Broadhurst <peter.broadhurst@kaleido.io>
@peterbroadhurst
peterbroadhurst requested a review from a team as a code owner August 29, 2026 14:49
@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
internal/contracts/manager.go 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: Peter Broadhurst <peter.broadhurst@kaleido.io>

@EnriqueL8 EnriqueL8 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @peterbroadhurst - looks good and clear explanation 👍🏼

@EnriqueL8
EnriqueL8 merged commit 4b516fd into main Sep 1, 2026
39 of 41 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.

2 participants