Skip to content
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

perf(api): Lookup subscriber preferences with a single database query #7119

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

Conversation

rifont
Copy link
Contributor

@rifont rifont commented Nov 24, 2024

What changed? Why was the change needed?

  • Lookup subscriber preferences with a single database query
  • Extract preference merging logic to a separate use-case for shared use

Screenshots

Expand for optional sections

Related enterprise PR

Special notes for your reviewer

Copy link

netlify bot commented Nov 24, 2024

Deploy Preview for novu-stg-vite-dashboard-poc ready!

Name Link
🔨 Latest commit 5b5f07c
🔍 Latest deploy log https://app.netlify.com/sites/novu-stg-vite-dashboard-poc/deploys/67442b133787230008b7fd21
😎 Deploy Preview https://deploy-preview-7119--novu-stg-vite-dashboard-poc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Comment on lines 56 to 83
// TODO: replace this runtime mapping with a single query to the database
const subscriberWorkflowPreferences = await Promise.all(
templateList.map(async (template) =>
this.getSubscriberTemplatePreferenceUsecase.execute(
GetSubscriberTemplatePreferenceCommand.create({
organizationId: command.organizationId,
subscriberId: command.subscriberId,
environmentId: command.environmentId,
template,
subscriber,
includeInactiveChannels: command.includeInactiveChannels,
}),
),
),
);
const allPreferences = await this.preferencesRepository.find({
_environmentId: command.environmentId,
$or: [
{
_subscriberId: command.subscriberId,
type: PreferencesTypeEnum.SUBSCRIBER_WORKFLOW,
},
{
_templateId: { $in: workflowList.map((template) => template._id) },
type: {
$in: [
PreferencesTypeEnum.WORKFLOW_RESOURCE,
PreferencesTypeEnum.USER_WORKFLOW,
],
},
},
],
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Et voila, a single database query!

@rifont rifont changed the title perf(preferences): Lookup subscriber preferences with a single database query perf(api): Lookup subscriber preferences with a single database query Nov 24, 2024
}),
),
),
const allPreferences = await this.preferencesRepository.find({
Copy link
Contributor

@SokratisVidros SokratisVidros Nov 25, 2024

Choose a reason for hiding this comment

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

❓ Is it maybe faster to split this into these into parallel queries, one for each Preference type?

*
* If the subscriber has no preferences, the workflow preferences are returned.
*/
export class MergePreferences {
Copy link
Contributor

Choose a reason for hiding this comment

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

😍 This is exactly what we needed in the code. A central place to do the resolution.

],
});

const subscriberGlobalPreferences = allPreferences.filter(
Copy link
Contributor

Choose a reason for hiding this comment

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

If we split it into separate Mongo queries, we can fetch this set of preferences from Mongo.

preference.type === PreferencesTypeEnum.SUBSCRIBER_GLOBAL,
);

const workflowPreferenceSets = allPreferences.reduce<
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto. If we split it into separate Mongo queries, we can fetch this set of preferences from Mongo.

command.includeInactiveChannels,
);

const initialChannels = filteredPreference(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we move these two processing functions into the MergeCommand to have everything in one place?

);

return nonCriticalWorkflowPreferences;
}

private getChannels(
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
private getChannels(
private getActiveChannels(

workflowResourcePreferences,
workflowUserPreferences,
]
.filter((preference) => preference !== undefined)
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
.filter((preference) => preference !== undefined)
.reduce((preference) => ...)

*/
export class MergePreferences {
public static merge(
command: MergePreferencesCommand,
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel the code would be simplified if we passed each type of preference separately to this use case. Currently, we fetch from Mongo, we do some mini-processing before calling the Merge use case, and then we combine them and split them again inside the use case.

My suggestion is:

  1. Fetch the four types of preferences separately (with a single or separate queries from Mongo).
  2. Feed them to MergeUseCase separately via the command.
  3. Do all the processing and merging in the MergePreferences use case.

const readOnlyFlag = workflowPreferences?.all?.readOnly;

// Determine the most specific preference applied
let mostSpecificPreference: PreferencesTypeEnum | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

We can get this automatically from deepMerge if we provide it during function invocation. The last type will win.

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