Skip to content

PIN-7197 - notification-tenant-lifecycle-consumer #2179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

tpetrucciani
Copy link
Collaborator

@tpetrucciani tpetrucciani commented Jul 22, 2025

This PR adds a new consumer that listens to tenant onboarding and deletion events and calls the notification-config-process APIs to create a default notification config for the new tenant or delete the notification config when the tenant is deleted

@tpetrucciani tpetrucciani changed the base branch from PIN-7226_notification_config_create_delete to develop July 22, 2025 08:56
@tpetrucciani tpetrucciani changed the base branch from develop to PIN-7226_notification_config_create_delete July 22, 2025 08:56
@tpetrucciani tpetrucciani force-pushed the PIN-7197_notification_tenant_lifecycle_consumer branch 2 times, most recently from b7c3574 to 84972e0 Compare July 23, 2025 09:44
@tpetrucciani tpetrucciani marked this pull request as ready for review July 23, 2025 13:25
Comment on lines +196 to +202
if (
(await tenantReadModelService.getTenantById(tenantId)) === undefined
) {
logger.warn(
`Tenant ${tenantId} not found, creating default notification configuration anyway (assuming the readmodel is not yet updated with the new tenant)`
);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We're not sure how to deal with this scenario. We'd like to ensure that the default notification config is only created for existing tenants and only deleted when the tenant is deleted. However, we're creating/deleting it in response to TenantOnboarded/MaintenanceTenantDeleted events, therefore it might occur when the readmodel isn't updated yet.

We decided just to log a warning in case the process doesn't see the tenant in the readmodel yet (and conversely, in the delete, if it still sees it).

This could help notice hypotetical issues in which we had spurious calls to these APIs, but, in practice, we expect these logs to occur just because this code is executed before the readmodel is updated, in which case it's OK (since anyway a TenantOnboarded event has occurred). So I'm not sure how useful this is.

We could have the creation or deletion fail outright in these cases, but then we'd force the notification-tenant-lifecycle-consumer to retry until it succeeds, which is maybe wasteful (and produces spurious error logs) since it's not technically an issue to create the config before the tenant.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that making the request succeed without performing the insertion could bring some extra activities, like triggering the endpoint again manually after the readmodel gets updated.
It might be easier to make the request fail, so that the consumer calling it will retry until it crashes. At that point we would be already aware to double-check if the tenant has been finally written in the readmodel and then restart the consumer without any other activity.
What do you think? We could just make an evaluation in dev to see how much this scenario is likely to happen.

Copy link
Contributor

@taglioni-r taglioni-r Jul 24, 2025

Choose a reason for hiding this comment

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

Sorry, I misunderstood this:

We decided just to log a warning in case the process [...]

So the notification config gets actually written, even though the tenant is not in readmodel yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

The only bad scenario would be a tenant retrieving its own notification config before the tenant itself is written in readmodel. If we avoid that read operation, it should be ok.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The only bad scenario would be a tenant retrieving its own notification config before the tenant itself is written in readmodel. If we avoid that read operation, it should be ok.

That's right. I suppose that, if the tenant isn't in the readmodel yet, the user would be unable to access the frontend or receive other errors anyway (e.g. because the frontend can't fetch the user's tenant to display it?).

The other hypothetical risk is creating spurious configs for tenants that don't exist if we somehow call the route with a non-existent id. But I think it's an unlikely bug and also, while creating some spurious configs that won't be used, doesn't have actual impacts.

For the delete, if by mistake it gets called for an existing tenant, we'd delete the configuration and, afterwards, that tenant wouldn't be notified (and users in that tenant would probably get an error if they try to access their notification configurations). But again I think it's an unlikely issue that we end up calling the route with a wrong ID that matches another tenant.

Comment on lines +50 to +58
await match(decodedMessage)
.with({ event_version: 1 }, () => {
loggerInstance.info(
`Discarding message with event_version = 1. Partition number: ${partition}. Offset: ${message.offset}`
);
})
.with({ event_version: 2 }, (msg) => {
loggerInstance.info(
`Processing ${decodedMessage.type} message - Partition number: ${partition}. Offset: ${message.offset}`
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We thought to handle only V2 tenant events, not V1, since it's a new service. For past data, we suppose we'll have a custom process anyway, without using the consumer, to avoid re-running all tenant events.

Does this make sense to you? Asking @galales too for confirmation.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that handling here all the events (past v1, past v2, incoming v2) would simplify the reprocess scenario, but up to @galales

Base automatically changed from PIN-7226_notification_config_create_delete to develop July 24, 2025 12:56
@tpetrucciani tpetrucciani force-pushed the PIN-7197_notification_tenant_lifecycle_consumer branch from 9783c0c to f67d341 Compare July 24, 2025 14:40
{ notificationConfigProcess }: PagoPAInteropBeClients
) {
return {
async processMessage(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
async processMessage(
async handleMessage(

I would rename this in order to be different from the one in index.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants