Skip to content

Commit

Permalink
fix: optimize ban user lookup with redis caching
Browse files Browse the repository at this point in the history
  • Loading branch information
titanism committed May 1, 2024
1 parent fab3143 commit 64b2536
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 43 deletions.
2 changes: 1 addition & 1 deletion app/controllers/api/v1/lookup.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async function lookup(ctx) {
return;
}

const bannedUserIdSet = await Users.getBannedUserIdSet(ctx);
const bannedUserIdSet = await Users.getBannedUserIdSet(ctx.client);

const username = isSANB(ctx.query.username)
? ctx.query.username.toLowerCase()
Expand Down
15 changes: 9 additions & 6 deletions app/models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -1107,21 +1107,24 @@ Users.index(
}
);

async function getBannedUserIdSet(ctx) {
async function getBannedUserIdSet(client) {
let bannedUserIds = [];
// TODO: uncomment this once redis performance gets better in prod
// bannedUserIds = await ctx.client.get('banned_user_ids');
if (bannedUserIds.length > 0) {
bannedUserIds = await client.get('banned_user_ids');
if (
bannedUserIds &&
typeof bannedUserIds === 'string' &&
bannedUserIds.length > 0
) {
bannedUserIds = new Set(JSON.parse(bannedUserIds));
} else {
bannedUserIds = await this.distinct('id', {
[config.userFields.isBanned]: true
});
ctx.client
client
.set('banned_user_ids', safeStringify(bannedUserIds), 'PX', ms('1h'))
.then()
.catch((err) => {
ctx.logger.fatal(err);
logger.fatal(err);
});
bannedUserIds = new Set(bannedUserIds);
}
Expand Down
10 changes: 7 additions & 3 deletions jobs/cleanup-aliases.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const { parentPort } = require('node:worker_threads');
require('#config/mongoose');

const Graceful = require('@ladjs/graceful');
const Redis = require('@ladjs/redis');
const pMap = require('p-map');
const sharedConfig = require('@ladjs/shared-config');

const mongoose = require('mongoose');
const config = require('#config');
Expand All @@ -23,9 +25,12 @@ const logger = require('#helpers/logger');
const setupMongoose = require('#helpers/setup-mongoose');
const { Aliases, Users, Domains } = require('#models');

const breeSharedConfig = sharedConfig('BREE');
const client = new Redis(breeSharedConfig.redis, logger);
const concurrency = Math.round(os.cpus().length * 2);
const graceful = new Graceful({
mongooses: [mongoose],
redisClients: [client],
logger
});

Expand Down Expand Up @@ -220,10 +225,9 @@ async function mapper(alias) {

logger.info('starting lowercase job');

// TODO: this would need optimized in the future if we ran it
try {
const bannedUserIds = await Users.distinct('_id', {
[config.userFields.isBanned]: true
});
const bannedUserIds = await Users.getBannedUserIdSet(client);
const ids = await Aliases.distinct('_id', {
user: { $nin: bannedUserIds }
});
Expand Down
34 changes: 16 additions & 18 deletions jobs/cleanup-database.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ const { parentPort } = require('node:worker_threads');
require('#config/mongoose');

const Graceful = require('@ladjs/graceful');
const Redis = require('@ladjs/redis');
const Stripe = require('stripe');
const dayjs = require('dayjs-with-plugins');
const sharedConfig = require('@ladjs/shared-config');
const mongoose = require('mongoose');

const env = require('#config/env');
Expand All @@ -26,10 +28,13 @@ const logger = require('#helpers/logger');
const setupMongoose = require('#helpers/setup-mongoose');
const { paypalAgent } = require('#helpers/paypal');

const breeSharedConfig = sharedConfig('BREE');
const client = new Redis(breeSharedConfig.redis, logger);
const stripe = new Stripe(env.STRIPE_SECRET_KEY);

const graceful = new Graceful({
mongooses: [mongoose],
redisClients: [client],
logger
});

Expand Down Expand Up @@ -188,25 +193,18 @@ graceful.listen();
}
}

// cancel subscriptions for banned users
const bannedUsersWithSubscriptions = await Users.find({
$or: [
{
[config.userFields.isBanned]: true,
[config.userFields.paypalSubscriptionID]: {
$exists: true
}
},
{
[config.userFields.isBanned]: true,
[config.userFields.stripeSubscriptionID]: {
$exists: true
}
}
]
});
const bannedUserIdSet = await Users.getBannedUserIdSet(client);

for (const user of bannedUsersWithSubscriptions) {
// cancel subscriptions for banned users
for (const id of bannedUserIdSet) {
// eslint-disable-next-line no-await-in-loop
const user = await Users.findById(id);
if (!user) continue;
if (
!user[config.userFields.paypalSubscriptionID] &&
!user[config.userFields.stripeSubscriptionID]
)
continue;
// paypal
if (user[config.userFields.paypalSubscriptionID]) {
try {
Expand Down
3 changes: 3 additions & 0 deletions jobs/cleanup-upgrade-reminders.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ graceful.listen();
logger.info('executing pipeline');
await pipeline.exec();

// clear banned cache
await client.del('banned_user_ids');

logger.info(JSON.stringify(emails, null, 2));
} catch (err) {
await logger.error(err);
Expand Down
28 changes: 17 additions & 11 deletions jobs/recipient-verification-email.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ const { parentPort } = require('node:worker_threads');
require('#config/mongoose');

const Graceful = require('@ladjs/graceful');
const Redis = require('@ladjs/redis');
const _ = require('lodash');
const pMap = require('p-map');
const shortID = require('mongodb-short-id');
const sharedConfig = require('@ladjs/shared-config');
const mongoose = require('mongoose');

const config = require('#config');
Expand All @@ -27,10 +29,13 @@ const setupMongoose = require('#helpers/setup-mongoose');
const { Users, Domains, Aliases } = require('#models');
const { encrypt } = require('#helpers/encrypt-decrypt');

const breeSharedConfig = sharedConfig('BREE');
const client = new Redis(breeSharedConfig.redis, logger);
const concurrency = os.cpus().length;

const graceful = new Graceful({
mongooses: [mongoose],
redisClients: [client],
logger
});

Expand Down Expand Up @@ -170,22 +175,23 @@ async function mapper(alias) {

logger.info('starting recipient verification emails');

const [bannedUserIds, paidDomainIds] = await Promise.all([
Users.distinct('_id', {
[config.userFields.isBanned]: true
}),
Domains.distinct('_id', {
plan: { $ne: 'free' },
has_mx_record: true,
has_txt_record: true
})
]);
const bannedUserIdSet = await Users.getBannedUserIdSet(client);

const paidDomainIds = await Domains.distinct('_id', {
plan: { $ne: 'free' },
has_mx_record: true,
has_txt_record: true
});

const aliases = await Aliases.aggregate([
{
$match: {
has_recipient_verification: true,
user: { $nin: bannedUserIds },
user: {
$nin: [...bannedUserIdSet].map(
(id) => new mongoose.Types.ObjectId(id)
)
},
domain: { $in: paidDomainIds }
}
},
Expand Down
12 changes: 8 additions & 4 deletions jobs/upgrade-reminder-email.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const sharedConfig = require('@ladjs/shared-config');
const { boolean } = require('boolean');
const mongoose = require('mongoose');

const config = require('#config');
const email = require('#helpers/email');
const logger = require('#helpers/logger');
const setupMongoose = require('#helpers/setup-mongoose');
Expand Down Expand Up @@ -128,9 +127,14 @@ async function mapper(upgradeReminder) {
try {
logger.info('starting upgrade reminder emails');

const bannedUserEmails = await Users.distinct('email', {
[config.userFields.isBanned]: true
});
const bannedUserIdSet = await Users.getBannedUserIdSet(client);

const bannedUserEmails = [];
for (const id of bannedUserIdSet) {
// eslint-disable-next-line no-await-in-loop
const user = await Users.findById(id);
if (user && user.email) bannedUserEmails.push(user.email);
}

let upgradeReminders = await UpgradeReminders.aggregate([
{
Expand Down

0 comments on commit 64b2536

Please sign in to comment.