From b92efe4cd944c97bf8310ca47d4edbef968cfaae Mon Sep 17 00:00:00 2001 From: Koni Marti Date: Sat, 30 Apr 2022 01:08:57 +0200 Subject: [PATCH] imap: add debouncer to the idler Add a debouncer to the idle mode. Avoid unnecessary idling when another job arrives within a certain time frame. For example, the ui sends three messages to the worker at the same time when we open a message (FlagMessage, FetchMessageBodyPart, and the FetchMessageHeaders). The debouncer prevents the unnecessary entering and leaving of the idle mode between those messages. Signed-off-by: Koni Marti Acked-by: Robin Jarry --- worker/imap/configure.go | 11 +++++++++++ worker/imap/idler.go | 34 +++++++++++++++++++++++----------- worker/imap/worker.go | 1 + 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/worker/imap/configure.go b/worker/imap/configure.go index c25600d..ddaaf93 100644 --- a/worker/imap/configure.go +++ b/worker/imap/configure.go @@ -45,7 +45,10 @@ func (w *IMAPWorker) handleConfigure(msg *types.Configure) error { w.config.user = u.User w.config.folders = msg.Config.Folders + w.config.idle_timeout = 10 * time.Second + w.config.idle_debounce = 10 * time.Millisecond + w.config.connection_timeout = 30 * time.Second w.config.keepalive_period = 0 * time.Second w.config.keepalive_probes = 3 @@ -63,6 +66,14 @@ func (w *IMAPWorker) handleConfigure(msg *types.Configure) error { value, err) } w.config.idle_timeout = val + case "idle-debounce": + val, err := time.ParseDuration(value) + if err != nil || val < 0 { + return fmt.Errorf( + "invalid idle-debounce value %v: %v", + value, err) + } + w.config.idle_debounce = val case "reconnect-maxwait": val, err := time.ParseDuration(value) if err != nil || val < 0 { diff --git a/worker/imap/idler.go b/worker/imap/idler.go index e9aecfd..bac690f 100644 --- a/worker/imap/idler.go +++ b/worker/imap/idler.go @@ -25,6 +25,7 @@ type idler struct { config imapConfig client *imapClient worker *types.Worker + last time.Time stop chan struct{} done chan error waiting bool @@ -63,20 +64,31 @@ func (i *idler) isReady() bool { func (i *idler) Start() { if i.isReady() { i.stop = make(chan struct{}) + go func() { defer logging.PanicHandler() - i.idleing = true - i.log("=>(idle)") - now := time.Now() - err := i.client.Idle(i.stop, - &client.IdleOptions{ - LogoutTimeout: 0, - PollInterval: 0, - }) - i.idleing = false - i.done <- err - i.log("elapsed ideling time:", time.Since(now)) + select { + case <-i.stop: + // debounce idle + i.log("=>(idle) [debounce]") + i.done <- nil + case <-time.After(i.config.idle_debounce): + // enter idle mode + i.idleing = true + i.log("=>(idle)") + now := time.Now() + err := i.client.Idle(i.stop, + &client.IdleOptions{ + LogoutTimeout: 0, + PollInterval: 0, + }) + i.idleing = false + i.done <- err + i.log("elapsed idle time:", + time.Since(now)) + } }() + } else if i.isWaiting() { i.log("not started: wait for idle to exit") } else { diff --git a/worker/imap/worker.go b/worker/imap/worker.go index 6e47530..1ff6341 100644 --- a/worker/imap/worker.go +++ b/worker/imap/worker.go @@ -43,6 +43,7 @@ type imapConfig struct { folders []string oauthBearer lib.OAuthBearer idle_timeout time.Duration + idle_debounce time.Duration reconnect_maxwait time.Duration // tcp connection parameters connection_timeout time.Duration