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

chore: Add the possibility for removing the watcher for the message collection #30381

Merged
merged 17 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
957fda2
chore: add a flag to disable db watchers on the message collection
MarcosSpessatto Sep 13, 2023
5e61b10
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Sep 13, 2023
be538b1
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Sep 15, 2023
b6327ff
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Sep 18, 2023
ef450c8
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Oct 26, 2023
1126a8a
Merge branch 'develop' into chore/remove-message-db-watcher-1
sampaiodiego Nov 3, 2023
de50690
Merge branch 'develop' into chore/remove-message-db-watcher-1
sampaiodiego Nov 7, 2023
23b67e3
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Nov 8, 2023
ce94c2a
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Nov 10, 2023
13e4a99
chore: remove unnecessary fn
MarcosSpessatto Nov 10, 2023
4504415
chore: publish events directly using broadcast
MarcosSpessatto Nov 10, 2023
4a92a42
chore: useless param
MarcosSpessatto Nov 10, 2023
62965d3
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Nov 10, 2023
62a7f29
chore: undo param change
MarcosSpessatto Nov 10, 2023
040c1af
chore: delete user working without relying on msg db watcher (#30435)
MarcosSpessatto Nov 10, 2023
a7a264e
Merge branch 'develop' into chore/remove-message-db-watcher-1
MarcosSpessatto Nov 20, 2023
b790dd8
Merge branch 'develop' into chore/remove-message-db-watcher-1
sampaiodiego Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions apps/meteor/app/lib/server/functions/deleteUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele

// Users without username can't do anything, so there is nothing to remove
if (user.username != null) {
let userToReplaceWhenUnlinking: IUser | null = null;
const nameAlias = i18n.t('Removed_User');
await relinquishRoomOwnerships(userId, subscribedRooms);

const messageErasureType = settings.get('Message_ErasureType');
const messageErasureType = settings.get<'Delete' | 'Unlink' | 'Keep'>('Message_ErasureType');
switch (messageErasureType) {
case 'Delete':
const store = FileUpload.getStore('Uploads');
Expand All @@ -68,12 +70,11 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele

break;
case 'Unlink':
const rocketCat = await Users.findOneById('rocket.cat');
const nameAlias = i18n.t('Removed_User');
if (!rocketCat?._id || !rocketCat?.username) {
userToReplaceWhenUnlinking = await Users.findOneById('rocket.cat');
if (!userToReplaceWhenUnlinking?._id || !userToReplaceWhenUnlinking?.username) {
break;
}
await Messages.unlinkUserId(userId, rocketCat?._id, rocketCat?.username, nameAlias);
await Messages.unlinkUserId(userId, userToReplaceWhenUnlinking?._id, userToReplaceWhenUnlinking?.username, nameAlias);
break;
}

Expand Down Expand Up @@ -104,8 +105,16 @@ export async function deleteUser(userId: string, confirmRelinquish = false, dele
await Integrations.disableByUserId(userId); // Disables all the integrations which rely on the user being deleted.

// Don't broadcast user.deleted for Erasure Type of 'Keep' so that messages don't disappear from logged in sessions
if (messageErasureType !== 'Keep') {
void api.broadcast('user.deleted', user);
if (messageErasureType === 'Delete') {
void api.broadcast('user.deleted', user, {
messageErasureType,
});
}
if (messageErasureType === 'Unlink' && userToReplaceWhenUnlinking) {
void api.broadcast('user.deleted', user, {
messageErasureType,
replaceByUser: { _id: userToReplaceWhenUnlinking._id, username: userToReplaceWhenUnlinking?.username, alias: nameAlias },
});
}
}

Expand Down
18 changes: 17 additions & 1 deletion apps/meteor/client/startup/UserDeleted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ import { ChatMessage } from '../../app/models/client';
import { Notifications } from '../../app/notifications/client';

Meteor.startup(() => {
Notifications.onLogged('Users:Deleted', ({ userId }) => {
Notifications.onLogged('Users:Deleted', ({ userId, messageErasureType, replaceByUser }) => {
if (messageErasureType === 'Unlink' && replaceByUser) {
return ChatMessage.update(
{
'u._id': userId,
},
{
$set: {
'alias': replaceByUser.alias,
'u._id': replaceByUser._id,
'u.username': replaceByUser.username,
'u.name': undefined,
},
},
{ multi: true },
);
}
ChatMessage.remove({
'u._id': userId,
});
Expand Down
3 changes: 2 additions & 1 deletion apps/meteor/server/modules/listeners/listeners.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ export class ListenersModule {
});
});

service.onEvent('user.deleted', ({ _id: userId }) => {
service.onEvent('user.deleted', ({ _id: userId }, data) => {
notifications.notifyLoggedInThisInstance('Users:Deleted', {
userId,
...data,
});
});

Expand Down
52 changes: 52 additions & 0 deletions apps/meteor/server/modules/watchers/lib/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { IMessage, SettingValue, IUser } from '@rocket.chat/core-typings';
import { Messages, Settings, Users } from '@rocket.chat/models';
import mem from 'mem';

const getSettingCached = mem(async (setting: string): Promise<SettingValue> => Settings.getValueById(setting), { maxAge: 10000 });

const getUserNameCached = mem(
async (userId: string): Promise<string | undefined> => {
const user = await Users.findOne<Pick<IUser, 'name'>>(userId, { projection: { name: 1 } });
return user?.name;
},
{ maxAge: 10000 },
);

export const broadcastMessageSentEvent = async ({
id,
data,
broadcastCallback,
}: {
id: IMessage['_id'];
broadcastCallback: (message: IMessage) => Promise<void>;
data?: IMessage;
}): Promise<void> => {
const message = data ?? (await Messages.findOneById(id));
if (!message) {
return;
}

if (message._hidden !== true && message.imported == null) {
const UseRealName = (await getSettingCached('UI_Use_Real_Name')) === true;

if (UseRealName) {
if (message.u?._id) {
const name = await getUserNameCached(message.u._id);
if (name) {
message.u.name = name;
}
}

if (message.mentions?.length) {
for await (const mention of message.mentions) {
const name = await getUserNameCached(mention._id);
if (name) {
mention.name = name;
}
}
}
}

void broadcastCallback(message);
}
};
26 changes: 17 additions & 9 deletions apps/meteor/server/modules/watchers/watchers.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { EventSignatures } from '@rocket.chat/core-services';
import { dbWatchersDisabled } from '@rocket.chat/core-services';
import type {
ISubscription,
IUser,
Expand Down Expand Up @@ -64,17 +65,17 @@ export function isWatcherRunning(): boolean {
return watcherStarted;
}

export function initWatchers(watcher: DatabaseWatcher, broadcast: BroadcastCallback): void {
const getSettingCached = mem(async (setting: string): Promise<SettingValue> => Settings.getValueById(setting), { maxAge: 10000 });
const getSettingCached = mem(async (setting: string): Promise<SettingValue> => Settings.getValueById(setting), { maxAge: 10000 });

const getUserNameCached = mem(
async (userId: string): Promise<string | undefined> => {
const user = await Users.findOne<Pick<IUser, 'name'>>(userId, { projection: { name: 1 } });
return user?.name;
},
{ maxAge: 10000 },
);
const getUserNameCached = mem(
async (userId: string): Promise<string | undefined> => {
const user = await Users.findOne<Pick<IUser, 'name'>>(userId, { projection: { name: 1 } });
return user?.name;
},
{ maxAge: 10000 },
);

const messageWatcher = (watcher: DatabaseWatcher, broadcast: BroadcastCallback): void => {
watcher.on<IMessage>(Messages.getCollectionName(), async ({ clientAction, id, data }) => {
switch (clientAction) {
case 'inserted':
Expand Down Expand Up @@ -110,6 +111,13 @@ export function initWatchers(watcher: DatabaseWatcher, broadcast: BroadcastCallb
break;
}
});
};

export function initWatchers(watcher: DatabaseWatcher, broadcast: BroadcastCallback): void {
const dbWatchersEnabled = !dbWatchersDisabled;
if (dbWatchersEnabled) {
messageWatcher(watcher, broadcast);
}

watcher.on<ISubscription>(Subscriptions.getCollectionName(), async ({ clientAction, id, data, diff }) => {
switch (clientAction) {
Expand Down
13 changes: 10 additions & 3 deletions ee/packages/ddp-client/src/types/streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,16 @@ export interface StreamerEvents {
{
key: 'Users:Deleted';
args: [
{
userId: IUser['_id'];
},
| {
userId: IUser['_id'];
messageErasureType: 'Delete';
replaceByUser?: never;
}
| {
userId: IUser['_id'];
messageErasureType: 'Unlink';
replaceByUser?: { _id: IUser['_id']; username: IUser['username']; alias: string };
},
];
},
{
Expand Down
2 changes: 1 addition & 1 deletion packages/core-services/src/LocalBroker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
import { InstanceStatus } from '@rocket.chat/models';

import { asyncLocalStorage } from '.';
import type { EventSignatures } from './Events';
import type { EventSignatures } from './events/Events';
import type { IBroker, IBrokerNode } from './types/IBroker';
import type { ServiceClass, IServiceClass } from './types/ServiceClass';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import type {
} from '@rocket.chat/core-typings';
import type { LicenseLimitKind } from '@rocket.chat/license';

import type { AutoUpdateRecord } from './types/IMeteor';
import type { AutoUpdateRecord } from '../types/IMeteor';

type ClientAction = 'inserted' | 'updated' | 'removed' | 'changed';

Expand Down Expand Up @@ -100,7 +100,17 @@ export type EventSignatures = {
'stream'([streamer, eventName, payload]: [string, string, any[]]): void;
'subscription'(data: { action: string; subscription: Partial<ISubscription> }): void;
'user.avatarUpdate'(user: Partial<IUser>): void;
'user.deleted'(user: Pick<IUser, '_id'>): void;
'user.deleted'(
user: Pick<IUser, '_id'>,
data:
| {
messageErasureType: 'Delete';
}
| {
messageErasureType: 'Unlink';
replaceByUser: { _id: IUser['_id']; username: IUser['username']; alias: string };
},
): void;
'user.deleteCustomStatus'(userStatus: IUserStatus): void;
'user.nameChanged'(user: Pick<IUser, '_id' | 'name' | 'username'>): void;
'user.realNameChanged'(user: Partial<IUser>): void;
Expand Down Expand Up @@ -273,4 +283,5 @@ export type EventSignatures = {
'command.updated'(command: string): void;
'command.removed'(command: string): void;
'actions.changed'(): void;
'message.sent'(message: IMessage): void;
};
12 changes: 12 additions & 0 deletions packages/core-services/src/events/listeners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { IMessage } from '@rocket.chat/core-typings';

import type { IServiceClass } from '../types/ServiceClass';

export const dbWatchersDisabled = ['yes', 'true'].includes(String(process.env.DISABLE_DB_WATCHERS).toLowerCase());

export const listenToMessageSentEvent = (service: IServiceClass, action: (message: IMessage) => Promise<void>): void => {
if (dbWatchersDisabled) {
return service.onEvent('message.sent', (message: IMessage) => action(message));
}
return service.onEvent('watch.messages', ({ message }) => action(message));
};
Comment on lines +7 to +12
Copy link
Member Author

Choose a reason for hiding this comment

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

@ggazzo @sampaiodiego I decided to create another event to handle the "manual" process of sending events instead of using the existing watch.messages.

3 changes: 2 additions & 1 deletion packages/core-services/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ import type { IVoipService } from './types/IVoipService';
export { asyncLocalStorage } from './lib/asyncLocalStorage';
export { MeteorError, isMeteorError } from './MeteorError';
export { api } from './api';
export { EventSignatures } from './Events';
export { EventSignatures } from './events/Events';
export { listenToMessageSentEvent, dbWatchersDisabled } from './events/listeners';
export { LocalBroker } from './LocalBroker';

export { IBroker, IBrokerNode, BaseMetricOptions, IServiceMetrics } from './types/IBroker';
Expand Down
2 changes: 1 addition & 1 deletion packages/core-services/src/lib/Api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EventSignatures } from '../Events';
import type { EventSignatures } from '../events/Events';
import type { IApiService } from '../types/IApiService';
import type { IBroker, IBrokerNode } from '../types/IBroker';
import type { IServiceClass } from '../types/ServiceClass';
Expand Down
2 changes: 1 addition & 1 deletion packages/core-services/src/types/IApiService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EventSignatures } from '../Events';
import type { EventSignatures } from '../events/Events';
import type { IBroker, IBrokerNode } from './IBroker';
import type { IServiceClass } from './ServiceClass';

Expand Down
2 changes: 1 addition & 1 deletion packages/core-services/src/types/IBroker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EventSignatures } from '../Events';
import type { EventSignatures } from '../events/Events';
import type { IServiceClass } from './ServiceClass';

export interface IBrokerNode {
Expand Down
2 changes: 1 addition & 1 deletion packages/core-services/src/types/ServiceClass.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EventEmitter } from 'events';

import type { EventSignatures } from '../Events';
import type { EventSignatures } from '../events/Events';
import { asyncLocalStorage } from '../lib/asyncLocalStorage';
import type { IApiService } from './IApiService';
import type { IBroker, IBrokerNode } from './IBroker';
Expand Down
Loading