Skip to content

Commit 1346054

Browse files
authored
fix(imap-notifications): ZMS-106: imap-connection notifiations handling remove lock, use queue instead (#954)
* imap-connection notifiations handling remove lock, use queue instead * remove reduntant check
1 parent 11b4fcb commit 1346054

File tree

1 file changed

+45
-39
lines changed

1 file changed

+45
-39
lines changed

imap-core/lib/imap-connection.js

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,7 @@ class IMAPConnection extends EventEmitter {
570570
let isSelected = mailbox => mailbox && conn.selected && conn.selected.mailbox && conn.selected.mailbox.toString() === mailbox.toString();
571571

572572
this._listenerData = {
573-
lock: false,
574573
cleared: false,
575-
pendingUpdate: false,
576574
callback(message) {
577575
let selectedMailbox = conn.selected && conn.selected.mailbox;
578576
if (conn._closing || conn._closed) {
@@ -604,29 +602,11 @@ class IMAPConnection extends EventEmitter {
604602
return;
605603
}
606604

607-
if (conn._listenerData.lock) {
608-
// Mark that we received a notification while locked, will re-check after lock is released
609-
conn._listenerData.pendingUpdate = true;
610-
return;
611-
}
612-
613-
conn._listenerData.lock = true;
614-
conn._listenerData.pendingUpdate = false;
615-
616605
conn._server.notifier.getUpdates(selectedMailbox, conn.selected.modifyIndex, (err, updates) => {
617606
if (!conn._listenerData || conn._listenerData.cleared) {
618607
// already logged out
619608
return;
620609
}
621-
conn._listenerData.lock = false;
622-
623-
// Helper to re-check if we missed notifications while locked
624-
let recheckPending = () => {
625-
if (conn._listenerData && !conn._listenerData.cleared && conn._listenerData.pendingUpdate) {
626-
conn._listenerData.pendingUpdate = false;
627-
setImmediate(() => conn._listenerData.callback());
628-
}
629-
};
630610

631611
if (err) {
632612
conn.logger.info(
@@ -639,13 +619,11 @@ class IMAPConnection extends EventEmitter {
639619
conn.id,
640620
err.message
641621
);
642-
recheckPending();
643622
return;
644623
}
645624

646625
// check if the same mailbox is still selected
647626
if (!isSelected(selectedMailbox) || !updates || !updates.length) {
648-
recheckPending();
649627
return;
650628
}
651629

@@ -656,14 +634,28 @@ class IMAPConnection extends EventEmitter {
656634
conn.selected.modifyIndex = updates[updates.length - 1].modseq;
657635
}
658636

659-
// append received notifications to the list
660-
conn.selected.notifications = conn.selected.notifications.concat(updates);
637+
if (!conn.selected.notificationQueueKeys) {
638+
conn.selected.notificationQueueKeys = new Set();
639+
}
640+
641+
let queued = 0;
642+
for (let update of updates) {
643+
let key = update && update._id ? update._id.toString() : '';
644+
if (!key || conn.selected.notificationQueueKeys.has(key)) {
645+
continue;
646+
}
647+
648+
conn.selected.notificationQueueKeys.add(key);
649+
conn.selected.notifications.push(update);
650+
queued++;
651+
}
652+
661653
if (conn.idling) {
662654
// when idling emit notifications immediately
663-
conn.emitNotifications();
655+
if (queued) {
656+
conn.emitNotifications();
657+
}
664658
}
665-
666-
recheckPending();
667659
});
668660
}
669661
};
@@ -682,7 +674,24 @@ class IMAPConnection extends EventEmitter {
682674

683675
// send notifications to client
684676
emitNotifications() {
685-
if (this.state !== 'Selected' || !this.selected || !this.selected.notifications.length) {
677+
if (this.state !== 'Selected' || !this.selected) {
678+
return;
679+
}
680+
681+
let notifications = this.selected.notifications || [];
682+
this.selected.notifications = [];
683+
684+
if (this.selected.notificationQueueKeys && notifications.length) {
685+
for (let i = 0, len = notifications.length; i < len; i++) {
686+
let update = notifications[i];
687+
let key = update && update._id ? update._id.toString() : '';
688+
if (key) {
689+
this.selected.notificationQueueKeys.delete(key);
690+
}
691+
}
692+
}
693+
694+
if (!notifications.length) {
686695
return;
687696
}
688697

@@ -697,16 +706,16 @@ class IMAPConnection extends EventEmitter {
697706
},
698707
'[%s] Pending notifications: %s',
699708
this.id,
700-
this.selected.notifications.length
709+
notifications.length
701710
);
702711

703712
// find UIDs that are both added and removed
704713
let added = new Set(); // added UIDs
705714
let removed = new Set(); // removed UIDs
706715
let skip = new Set(); // UIDs that are removed before ever seen
707716

708-
for (let i = 0, len = this.selected.notifications.length; i < len; i++) {
709-
let update = this.selected.notifications[i];
717+
for (let i = 0, len = notifications.length; i < len; i++) {
718+
let update = notifications[i];
710719
if (update.command === 'EXISTS') {
711720
added.add(update.uid);
712721
} else if (update.command === 'EXPUNGE') {
@@ -722,20 +731,20 @@ class IMAPConnection extends EventEmitter {
722731

723732
// filter multiple FETCH calls, only keep latest, otherwise might mess up MODSEQ responses
724733
let fetches = new Set();
725-
for (let i = this.selected.notifications.length - 1; i >= 0; i--) {
726-
let update = this.selected.notifications[i];
734+
for (let i = notifications.length - 1; i >= 0; i--) {
735+
let update = notifications[i];
727736
if (update.command === 'FETCH') {
728737
// skip multiple flag updates and updates for removed or newly added messages
729738
if (fetches.has(update.uid) || added.has(update.uid) || removed.has(update.uid)) {
730-
this.selected.notifications.splice(i, 1);
739+
notifications.splice(i, 1);
731740
} else {
732741
fetches.add(update.uid);
733742
}
734743
}
735744
}
736745

737-
for (let i = 0, len = this.selected.notifications.length; i < len; i++) {
738-
let update = this.selected.notifications[i];
746+
for (let i = 0, len = notifications.length; i < len; i++) {
747+
let update = notifications[i];
739748

740749
// skip unnecessary entries that are already removed
741750
if (skip.has(update.uid)) {
@@ -828,9 +837,6 @@ class IMAPConnection extends EventEmitter {
828837
]
829838
});
830839
}
831-
832-
// clear queue
833-
this.selected.notifications = [];
834840
}
835841

836842
formatResponse(command, uid, data) {

0 commit comments

Comments
 (0)