Skip to content

Commit

Permalink
Merge pull request #136 from TogetherCrew/development
Browse files Browse the repository at this point in the history
Memory Heap Issue due to mongoose connection
  • Loading branch information
cyri113 authored Nov 30, 2023
2 parents adf3e44 + fbbd989 commit 5d55f60
Show file tree
Hide file tree
Showing 17 changed files with 100 additions and 116 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ src/config/*.env

# compiled output
/dist
/lib
/node_modules
/lib

Expand Down
57 changes: 42 additions & 15 deletions src/database/connection.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,46 @@
import { Connection } from 'mongoose';
import parentLogger from '../config/logger';
import mongoose, { Connection } from 'mongoose';
import {
heatMapSchema,
rawInfoSchema,
MemberActivitySchema,
guildMemberSchema,
channelSchema,
roleSchema,
type IHeatMap,
type IRawInfo,
type IMemberActivity,
type IGuildMember,
type IChannel,
type IRole,
} from '@togethercrew.dev/db';
import { type Snowflake } from 'discord.js';

const logger = parentLogger.child({ module: 'Connection' });
export default class DatabaseManager {
private static instance: DatabaseManager;
private modelCache: Record<string, boolean> = {};
public static getInstance(): DatabaseManager {
if (typeof DatabaseManager.instance === 'undefined') {
DatabaseManager.instance = new DatabaseManager();
}
return DatabaseManager.instance;
}

public getTenantDb(tenantId: Snowflake): Connection {
const dbName = tenantId;
const db = mongoose.connection.useDb(dbName, { useCache: true });
this.setupModels(db);
return db;
}

/**
* Closes a given Mongoose connection.
* @param {Connection} connection - The Mongoose connection object to be closed.
* @returns {Promise<void>} - A promise that resolves when the connection has been successfully closed.
* @throws {MongooseError} - If there is an error closing the connection, it is logged to the console and the error is thrown.
*/
export async function closeConnection(connection: Connection) {
try {
await connection.close();
logger.info({ database: connection.name }, 'The connection to database has been successfully closed');
} catch (error) {
logger.fatal({ database: connection.name, error }, 'Failed to close the connection to the database');
private setupModels(db: Connection): void {
if (!this.modelCache[db.name]) {
db.model<IHeatMap>('HeatMap', heatMapSchema);
db.model<IRawInfo>('RawInfo', rawInfoSchema);
db.model<IMemberActivity>('MemberActivity', MemberActivitySchema);
db.model<IGuildMember>('GuildMember', guildMemberSchema);
db.model<IChannel>('Channel', channelSchema);
db.model<IRole>('Role', roleSchema);
this.modelCache[db.name] = true;
}
}
}
10 changes: 3 additions & 7 deletions src/events/channel/channelCreate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js';
import { channelService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'ChannelCreate' });
Expand All @@ -14,14 +12,12 @@ export default {
if (channel instanceof TextChannel || channel instanceof VoiceChannel || channel instanceof CategoryChannel) {
const logFields = { guild_id: channel.guild.id, channel_id: channel.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(channel.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(channel.guild.id);
try {
await channelService.handelChannelChanges(connection, channel);
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle channel changes');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
}
},
Expand Down
11 changes: 4 additions & 7 deletions src/events/channel/channelDelete.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js';
import { channelService, guildService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'ChannelDelete' });
Expand All @@ -14,7 +12,7 @@ export default {
if (channel instanceof TextChannel || channel instanceof VoiceChannel || channel instanceof CategoryChannel) {
const logFields = { guild_id: channel.guild.id, channel_id: channel.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(channel.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(channel.guild.id);
try {
const channelDoc = await channelService.getChannel(connection, { channelId: channel.id });
await channelDoc?.softDelete();
Expand All @@ -23,11 +21,10 @@ export default {
selectedChannel => selectedChannel.channelId !== channel.id
);
await guildService.updateGuild({ guildId: channel.guild.id }, { selectedChannels: updatedSelecetdChannels });
logger.info(logFields, 'event is done');

} catch (err) {
logger.error({ ...logFields, err }, 'Failed to soft delete the channel');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
}
},
Expand Down
11 changes: 4 additions & 7 deletions src/events/channel/channelUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, Channel, TextChannel, VoiceChannel, CategoryChannel } from 'discord.js';
import { channelService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'ChannelUpdate' });
Expand All @@ -18,14 +16,13 @@ export default {
) {
const logFields = { guild_id: newChannel.guild.id, channel_id: newChannel.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(newChannel.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(newChannel.guild.id);
try {
await channelService.handelChannelChanges(connection, newChannel);
logger.info(logFields, 'event is done');

} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle channel changes');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
}
},
Expand Down
9 changes: 3 additions & 6 deletions src/events/client/ready.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Events, Client } from 'discord.js';
import { databaseService } from '@togethercrew.dev/db';
import { guildService } from '../../database/services';
import fetchMembers from '../../functions/fetchMembers';
import fetchChannels from '../../functions/fetchChannels';
import fetchRoles from '../../functions/fetchRoles';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';
import config from '../../config';

const logger = parentLogger.child({ event: 'ClientReady' });

Expand All @@ -17,7 +15,7 @@ export default {
logger.info('event is running');
const guilds = await guildService.getGuilds({ isDisconnected: false });
for (let i = 0; i < guilds.length; i++) {
const connection = databaseService.connectionFactory(guilds[i].guildId, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(guilds[i].guildId);
try {
logger.info({ guild_id: guilds[i].guildId }, 'Fetching guild members, roles,and channels');
await fetchMembers(connection, client, guilds[i].guildId);
Expand All @@ -26,8 +24,7 @@ export default {
logger.info({ guild_id: guilds[i].guildId }, 'Fetching guild members, roles, channels is done');
} catch (err) {
logger.error({ guild_id: guilds[i].guildId, err }, 'Fetching guild members, roles,and channels failed');
} finally {
await closeConnection(connection);
logger.info('event is done');
}
}
logger.info('event is done');
Expand Down
10 changes: 3 additions & 7 deletions src/events/member/guildMemberAdd.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, GuildMember } from 'discord.js';
import { guildMemberService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'GuildMemberAdd' });
Expand All @@ -13,14 +11,12 @@ export default {
async execute(member: GuildMember) {
const logFields = { guild_id: member.guild.id, guild_member_id: member.user.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(member.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(member.guild.id);
try {
await guildMemberService.handelGuildMemberChanges(connection, member);
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle guild member changes');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
},
};
10 changes: 3 additions & 7 deletions src/events/member/guildMemberRemove.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, GuildMember } from 'discord.js';
import { guildMemberService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'GuildMemberRemove' });
Expand All @@ -13,15 +11,13 @@ export default {
async execute(member: GuildMember) {
const logFields = { guild_id: member.guild.id, guild_member_id: member.user.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(member.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(member.guild.id);
try {
const guildMemberDoc = await guildMemberService.getGuildMember(connection, { discordId: member.user.id });
await guildMemberDoc?.softDelete();
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to soft delete the guild member');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
},
};
11 changes: 4 additions & 7 deletions src/events/member/guildMemberUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Events, GuildMember } from 'discord.js';
import { guildMemberService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';

import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'GuildMemberUpdate' });
Expand All @@ -13,14 +12,12 @@ export default {
async execute(oldMember: GuildMember, newMember: GuildMember) {
const logFields = { guild_id: newMember.guild.id, guild_member_id: newMember.user.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(newMember.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(newMember.guild.id);
try {
await guildMemberService.handelGuildMemberChanges(connection, newMember);
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle guild member changes');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
},
};
10 changes: 3 additions & 7 deletions src/events/role/roleCreate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, Role } from 'discord.js';
import { roleService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'GuildRoleCreate' });
Expand All @@ -13,14 +11,12 @@ export default {
async execute(role: Role) {
const logFields = { guild_id: role.guild.id, role_id: role.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(role.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(role.guild.id);
try {
await roleService.handelRoleChanges(connection, role);
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle role changes');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
},
};
11 changes: 4 additions & 7 deletions src/events/role/roleDelete.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Events, Role } from 'discord.js';
import { roleService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';

import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'GuildRoleDelete' });
Expand All @@ -13,15 +12,13 @@ export default {
async execute(role: Role) {
const logFields = { guild_id: role.guild.id, role_id: role.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(role.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(role.guild.id);
try {
const roleDoc = await roleService.getRole(connection, { roleId: role.id });
await roleDoc?.softDelete();
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to soft delete the role');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
},
};
10 changes: 3 additions & 7 deletions src/events/role/roleUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, Role } from 'discord.js';
import { roleService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'GuildRoleUpdate' });
Expand All @@ -13,14 +11,12 @@ export default {
async execute(oldRole: Role, newRole: Role) {
const logFields = { guild_id: newRole.guild.id, role_id: newRole.id };
logger.info(logFields, 'event is running');
const connection = databaseService.connectionFactory(newRole.guild.id, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(newRole.guild.id);
try {
await roleService.handelRoleChanges(connection, newRole);
logger.info(logFields, 'event is done');
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle role changes');
} finally {
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
},
};
9 changes: 3 additions & 6 deletions src/events/user/userUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Events, User } from 'discord.js';
import { guildMemberService, guildService } from '../../database/services';
import { databaseService } from '@togethercrew.dev/db';
import config from '../../config';
import { closeConnection } from '../../database/connection';
import DatabaseManager from '../../database/connection';
import parentLogger from '../../config/logger';

const logger = parentLogger.child({ event: 'UserUpdate' });
Expand All @@ -15,7 +13,7 @@ export default {
try {
const guilds = await guildService.getGuilds({});
for (let i = 0; i < guilds.length; i++) {
const connection = databaseService.connectionFactory(guilds[i].guildId, config.mongoose.dbURL);
const connection = DatabaseManager.getInstance().getTenantDb(guilds[i].guildId);
await guildMemberService.updateGuildMember(
connection,
{ discordId: newUser.id },
Expand All @@ -24,11 +22,10 @@ export default {
globalName: newUser.globalName,
}
);
await closeConnection(connection);
logger.info(logFields, 'event is done');
}
} catch (err) {
logger.error({ ...logFields, err }, 'Failed to handle user changes');
}
logger.info(logFields, 'event is done');
},
};
Loading

0 comments on commit 5d55f60

Please sign in to comment.