diff options
author | Koni Marti <koni.marti@gmail.com> | 2024-02-25 12:43:55 +0100 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2024-02-26 09:26:45 +0100 |
commit | d8d5fc8d31f358c6425dfa18a9f8d2767bc6df40 (patch) | |
tree | fad404f8451c9d44cb8e9ac61fae2b70817bd2fc /worker/imap | |
parent | 83c3a4051b146651c9e56924bf8e4a9c8b01c5a8 (diff) | |
download | aerc-d8d5fc8d31f358c6425dfa18a9f8d2767bc6df40.tar.gz |
imap: drain updates channel when deleting messages
Drain the buffered updates channel when deleting messages to prevent a
backend freeze. Unilateral update messages from the IMAP server can fill
up the buffered channel if not handled during a large operation (more
than ~50 messages).
Link: https://lists.sr.ht/~rjarry/aerc-discuss/%3CCZCPBTWI3PIW.T8MWNCBG7FGL%40disroot.org%3E
Changelog-fixed: Prevent a freeze for large-scale deletions with IMAP.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Jeremy Baxter <jtbx@disroot.org>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'worker/imap')
-rw-r--r-- | worker/imap/flags.go | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/worker/imap/flags.go b/worker/imap/flags.go index 06680818..60137bd3 100644 --- a/worker/imap/flags.go +++ b/worker/imap/flags.go @@ -2,13 +2,53 @@ package imap import ( "github.com/emersion/go-imap" + "github.com/emersion/go-imap/client" "git.sr.ht/~rjarry/aerc/lib/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" ) +// drainUpdates will drain the updates channel. For some operations, the imap +// server will send unilateral messages. If they arrive while another operation +// is in progress, the buffered updates channel can fill up and cause a freeze +// of the entire backend. Avoid this by draining the updates channel and only +// process the Message and Expunge updates. +// +// To stop the draining, close the returned struct. +func (imapw *IMAPWorker) drainUpdates() *drainCloser { + done := make(chan struct{}) + go func() { + defer log.PanicHandler() + for { + select { + case update := <-imapw.updates: + switch update.(type) { + case *client.MessageUpdate, + *client.ExpungeUpdate: + imapw.handleImapUpdate(update) + } + case <-done: + return + } + } + }() + return &drainCloser{done} +} + +type drainCloser struct { + done chan struct{} +} + +func (d *drainCloser) Close() error { + close(d.done) + return nil +} + func (imapw *IMAPWorker) handleDeleteMessages(msg *types.DeleteMessages) { + drain := imapw.drainUpdates() + defer drain.Close() + item := imap.FormatFlagsOp(imap.AddFlags, true) flags := []interface{}{imap.DeletedFlag} uids := toSeqSet(msg.Uids) |