Skip to content

Commit

Permalink
[FIX] Performance issues when running Omnichannel job queue dispatcher (
Browse files Browse the repository at this point in the history
#23661)

* Fix Omnichannel job queue dispatcher.

* Keep settings validation.

* Remove console.log.

* Review requests.

* Fix default setting value.
  • Loading branch information
renatobecker authored and ggazzo committed Nov 8, 2021
1 parent 62d219a commit caacb04
Show file tree
Hide file tree
Showing 19 changed files with 108 additions and 40 deletions.
6 changes: 4 additions & 2 deletions app/livechat/server/lib/Helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,18 @@ export const createLivechatRoom = (rid, name, guest, roomInfo = {}, extraData =

const extraRoomInfo = callbacks.run('livechat.beforeRoom', roomInfo, extraData);
const { _id, username, token, department: departmentId, status = 'online' } = guest;
const newRoomAt = new Date();

logger.debug(`Creating livechat room for visitor ${ _id }`);

const room = Object.assign({
_id: rid,
msgs: 0,
usersCount: 1,
lm: new Date(),
lm: newRoomAt,
fname: name,
t: 'l',
ts: new Date(),
ts: newRoomAt,
departmentId,
v: {
_id,
Expand All @@ -67,6 +68,7 @@ export const createLivechatRoom = (rid, name, guest, roomInfo = {}, extraData =
type: OmnichannelSourceType.OTHER,
alias: 'unknown',
},
queuedAt: newRoomAt,
}, extraRoomInfo);

const roomId = Rooms.insert(room);
Expand Down
2 changes: 1 addition & 1 deletion app/livechat/server/lib/QueueManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { callbacks } from '../../../callbacks/server';
import { Logger } from '../../../logger';
import { RoutingManager } from './RoutingManager';

const logger = new Logger('QueueMananger');
const logger = new Logger('QueueManager');

export const saveQueueInquiry = (inquiry) => {
LivechatInquiry.queueInquiry(inquiry._id);
Expand Down
16 changes: 12 additions & 4 deletions app/models/server/models/LivechatInquiry.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class LivechatInquiry extends Base {
this.update({
_id: inquiryId,
}, {
$set: { status: 'taken' },
$set: { status: 'taken', takenAt: new Date() },
$unset: { defaultAgent: 1, estimatedInactivityCloseTimeAt: 1 },
});
}
Expand All @@ -71,9 +71,17 @@ export class LivechatInquiry extends Base {
return this.update({
_id: inquiryId,
}, {
$set: {
status: 'queued',
},
$set: { status: 'queued', queuedAt: new Date() },
$unset: { takenAt: 1 },
});
}

queueInquiryAndRemoveDefaultAgent(inquiryId) {
return this.update({
_id: inquiryId,
}, {
$set: { status: 'queued', queuedAt: new Date() },
$unset: { takenAt: 1, defaultAgent: 1 },
});
}

Expand Down
6 changes: 3 additions & 3 deletions app/models/server/models/LivechatRooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class LivechatRooms extends Base {
this.tryEnsureIndex({ 'v.token': 1 }, { sparse: true });
this.tryEnsureIndex({ 'v.token': 1, 'email.thread': 1 }, { sparse: true });
this.tryEnsureIndex({ 'v._id': 1 }, { sparse: true });
this.tryEnsureIndex({ t: 1, departmentId: 1, closedAt: 1 }, { partialFilterExpression: { closedAt: { $exists: true } } });
}

findLivechat(filter = {}, offset = 0, limit = 20) {
Expand Down Expand Up @@ -717,9 +718,8 @@ export class LivechatRooms extends Base {
t: 'l',
};
const update = {
$unset: {
servedBy: 1,
},
$set: { queuedAt: new Date() },
$unset: { servedBy: 1 },
};

this.update(query, update);
Expand Down
4 changes: 1 addition & 3 deletions app/models/server/raw/Rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ export class RoomsRaw extends BaseRaw {
{
$match: {
t: 'l',
closedAt: { $exists: true },
metrics: { $exists: true },
'metrics.chatDuration': { $exists: true },
...department && { departmentId: department },
closedAt: { $exists: true },
},
},
{ $sort: { closedAt: -1 } },
Expand Down
5 changes: 5 additions & 0 deletions app/ui-sidenav/client/roomList.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ const mergeSubRoom = (subscription) => {
livechatData: 1,
departmentId: 1,
source: 1,
queuedAt: 1,
},
};

Expand Down Expand Up @@ -212,6 +213,7 @@ const mergeSubRoom = (subscription) => {
departmentId,
ts,
source,
queuedAt,
} = room;

subscription.lm = subscription.lr ? new Date(Math.max(subscription.lr, lastRoomUpdate)) : lastRoomUpdate;
Expand Down Expand Up @@ -249,6 +251,7 @@ const mergeSubRoom = (subscription) => {
departmentId,
ts,
source,
queuedAt,
});
};

Expand Down Expand Up @@ -291,6 +294,7 @@ const mergeRoomSub = (room) => {
departmentId,
ts,
source,
queuedAt,
} = room;

Subscriptions.update({
Expand Down Expand Up @@ -328,6 +332,7 @@ const mergeRoomSub = (room) => {
jitsiTimeout,
ts,
source,
queuedAt,
...getLowerCaseNames(room, sub.name, sub.fname),
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function ChatInfo({ id, route }) {
priorityId,
livechatData,
source,
queuedAt,
} = room || { room: { v: {} } };

const routePath = useRoute(route || 'omnichannel-directory');
Expand All @@ -58,6 +59,7 @@ function ChatInfo({ id, route }) {
const hasGlobalEditRoomPermission = hasPermission('save-others-livechat-room-info');
const hasLocalEditRoomPermission = servedBy?._id === Meteor.userId();
const visitorId = v?._id;
const queueStartedAt = queuedAt || ts;

const dispatchToastMessage = useToastMessageDispatch();
useEffect(() => {
Expand Down Expand Up @@ -126,13 +128,13 @@ function ChatInfo({ id, route }) {
<Info>{topic}</Info>
</Field>
)}
{ts && (
{queueStartedAt && (
<Field>
<Label>{t('Queue_Time')}</Label>
{servedBy ? (
<Info>{moment(servedBy.ts).from(moment(ts), true)}</Info>
<Info>{moment(servedBy.ts).from(moment(queueStartedAt), true)}</Info>
) : (
<Info>{moment(ts).fromNow(true)}</Info>
<Info>{moment(queueStartedAt).fromNow(true)}</Info>
)}
</Field>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function ChatInfoDirectory({ id, route, room }) {
responseBy,
priorityId,
livechatData,
queuedAt,
} = room || { room: { v: {} } };

const routePath = useRoute(route || 'omnichannel-directory');
Expand All @@ -53,6 +54,7 @@ function ChatInfoDirectory({ id, route, room }) {
const hasGlobalEditRoomPermission = hasPermission('save-others-livechat-room-info');
const hasLocalEditRoomPermission = servedBy?._id === Meteor.userId();
const visitorId = v?._id;
const queueStartedAt = queuedAt || ts;

const dispatchToastMessage = useToastMessageDispatch();
useEffect(() => {
Expand Down Expand Up @@ -120,13 +122,13 @@ function ChatInfoDirectory({ id, route, room }) {
<Info>{topic}</Info>
</Field>
)}
{ts && (
{queueStartedAt && (
<Field>
<Label>{t('Queue_Time')}</Label>
{servedBy ? (
<Info>{moment(servedBy.ts).from(moment(ts), true)}</Info>
<Info>{moment(servedBy.ts).from(moment(queueStartedAt), true)}</Info>
) : (
<Info>{moment(ts).fromNow(true)}</Info>
<Info>{moment(queueStartedAt).fromNow(true)}</Info>
)}
</Field>
)}
Expand Down
1 change: 1 addition & 0 deletions definition/IRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export interface IOmnichannelRoom extends Omit<IRoom, 'default' | 'featured' | '
responseBy: any;
priorityId: any;
livechatData: any;
queuedAt?: Date;
}

export const isOmnichannelRoom = (room: IRoom): room is IOmnichannelRoom & IRoom => room.t === 'l';
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ callbacks.add('livechat.afterTakeInquiry', async (inquiry) => {
const { department } = inquiry;
debouncedDispatchWaitingQueueStatus(department);

cbLogger.debug(`Statuses for queue ${ department || 'Public' } updated succesfully`);
cbLogger.debug(`Statuses for queue ${ department || 'Public' } updated successfully`);
return inquiry;
}, callbacks.priority.MEDIUM, 'livechat-after-take-inquiry');
10 changes: 6 additions & 4 deletions ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ callbacks.add('livechat.beforeRouteChat', async (inquiry, agent) => {

saveQueueInquiry(inquiry);

const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ _id, department });
if (inq) {
dispatchInquiryPosition(inq);
if (settings.get('Omnichannel_calculate_dispatch_service_queue_statistics')) {
const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ _id, department });
if (inq) {
dispatchInquiryPosition(inq);
cbLogger.debug(`Callback success. Inquiry ${ _id } position has been notified`);
}
}

cbLogger.debug(`Callback success. Inquiry ${ _id } position has been notified`);
return LivechatInquiry.findOneById(_id);
}, callbacks.priority.HIGH, 'livechat-before-routing-chat');
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ const handleOnAgentAssignmentFailed = async ({ inquiry, room, options }: { inqui
const { _id: roomId } = room;

const { _id: inquiryId } = inquiry;
LivechatInquiry.queueInquiry(inquiryId);
LivechatInquiry.removeDefaultAgentById(inquiryId);
LivechatInquiry.queueInquiryAndRemoveDefaultAgent(inquiryId);
LivechatRooms.removeAgentByRoomId(roomId);
Subscriptions.removeByRoomId(roomId);
dispatchAgentDelegated(roomId, null);
Expand Down
6 changes: 1 addition & 5 deletions ee/app/livechat-enterprise/server/hooks/onCloseLivechat.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { callbacks } from '../../../../../app/callbacks';
import { settings } from '../../../../../app/settings/server';
import { debouncedDispatchWaitingQueueStatus } from '../lib/Helper';
import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager';
import { LivechatEnterprise } from '../lib/LivechatEnterprise';

const onCloseLivechat = (room) => {
Expand All @@ -12,10 +11,7 @@ const onCloseLivechat = (room) => {
}

const { departmentId } = room || {};
if (!RoutingManager.getConfig().autoAssignAgent) {
debouncedDispatchWaitingQueueStatus(departmentId);
return room;
}
debouncedDispatchWaitingQueueStatus(departmentId);

return room;
};
Expand Down
18 changes: 14 additions & 4 deletions ee/app/livechat-enterprise/server/lib/Helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,17 @@ export const dispatchInquiryPosition = async (inquiry, queueInfo) => {
};

export const dispatchWaitingQueueStatus = async (department) => {
if (!settings.get('Livechat_waiting_queue') && !settings.get('Omnichannel_calculate_dispatch_service_queue_statistics')) {
return;
}

helperLogger.debug(`Updating statuses for queue ${ department || 'Public' }`);
const queue = await LivechatInquiry.getCurrentSortedQueueAsync({ department });

if (!queue.length) {
return;
}

const queueInfo = await getQueueInfo(department);
queue.forEach((inquiry) => {
dispatchInquiryPosition(inquiry, queueInfo);
Expand Down Expand Up @@ -126,14 +135,11 @@ export const processWaitingQueue = async (department) => {

if (room && room.servedBy) {
const { _id: rid, servedBy: { _id: agentId } } = room;
helperLogger.debug(`Inquiry ${ inquiry._id } taken succesfully by agent ${ agentId }. Notifying`);
helperLogger.debug(`Inquiry ${ inquiry._id } taken successfully by agent ${ agentId }. Notifying`);
return setTimeout(() => {
propagateAgentDelegated(rid, agentId);
}, 1000);
}

const { departmentId } = room || {};
await debouncedDispatchWaitingQueueStatus(departmentId);
};

export const setPredictedVisitorAbandonmentTime = (room) => {
Expand Down Expand Up @@ -254,6 +260,10 @@ export const getLivechatQueueInfo = async (room) => {
return null;
}

if (!settings.get('Omnichannel_calculate_dispatch_service_queue_statistics')) {
return null;
}

const { _id: rid, departmentId: department } = room;
const inquiry = LivechatInquiry.findOneByRoomId(rid, { fields: { _id: 1, status: 1 } });
if (!inquiry) {
Expand Down
11 changes: 8 additions & 3 deletions ee/app/livechat-enterprise/server/lib/LivechatEnterprise.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ export const LivechatEnterprise = {
},
};

const RACE_TIMEOUT = 1000;
const DEFAULT_RACE_TIMEOUT = 5000;
let queueDelayTimeout = DEFAULT_RACE_TIMEOUT;

const queueWorker = {
running: false,
Expand Down Expand Up @@ -242,9 +243,9 @@ const queueWorker = {
}

const queue = await this.nextQueue();
queueLogger.debug(`Executing queue ${ queue || 'Public' } with timeout of ${ RACE_TIMEOUT }`);
queueLogger.debug(`Executing queue ${ queue || 'Public' } with timeout of ${ queueDelayTimeout }`);

setTimeout(this.checkQueue.bind(this, queue), RACE_TIMEOUT);
setTimeout(this.checkQueue.bind(this, queue), queueDelayTimeout);
},

async checkQueue(queue) {
Expand Down Expand Up @@ -292,3 +293,7 @@ settings.watch('Livechat_enabled', (enabled) => {
omnichannelIsEnabled = enabled;
omnichannelIsEnabled && RoutingManager.isMethodSet() ? shouldQueueStart() : queueWorker.stop();
});

settings.watch('Omnichannel_queue_delay_timeout', (timeout) => {
queueDelayTimeout = timeout < 1 ? DEFAULT_RACE_TIMEOUT : timeout * 1000;
});
31 changes: 31 additions & 0 deletions ee/app/livechat-enterprise/server/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,37 @@ export const createSettings = (): void => {
],
});

this.add('Omnichannel_calculate_dispatch_service_queue_statistics', true, {
type: 'boolean',
group: 'Omnichannel',
section: 'Queue_management',
i18nLabel: 'Omnichannel_calculate_dispatch_service_queue_statistics',
enableQuery: [{ _id: 'Livechat_waiting_queue', value: true }, omnichannelEnabledQuery],
enterprise: true,
invalidValue: false,
modules: [
'livechat-enterprise',
],
});

this.add('Omnichannel_queue_delay_timeout', 5, {
type: 'int',
group: 'Omnichannel',
section: 'Queue_management',
i18nLabel: 'Queue_delay_timeout',
i18nDescription: 'Time_in_seconds',
enableQuery: [
{ _id: 'Livechat_waiting_queue', value: true },
{ _id: 'Livechat_Routing_Method', value: { $ne: 'Manual_Selection' } },
omnichannelEnabledQuery,
],
enterprise: true,
invalidValue: 5,
modules: [
'livechat-enterprise',
],
});

this.add('Livechat_number_most_recent_chats_estimate_wait_time', 100, {
type: 'int',
group: 'Omnichannel',
Expand Down
Loading

0 comments on commit caacb04

Please sign in to comment.