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 <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
Koni Marti 2022-04-30 01:08:57 +02:00 committed by Robin Jarry
parent e5b339702a
commit b92efe4cd9
3 changed files with 35 additions and 11 deletions

View file

@ -45,7 +45,10 @@ func (w *IMAPWorker) handleConfigure(msg *types.Configure) error {
w.config.user = u.User w.config.user = u.User
w.config.folders = msg.Config.Folders w.config.folders = msg.Config.Folders
w.config.idle_timeout = 10 * time.Second w.config.idle_timeout = 10 * time.Second
w.config.idle_debounce = 10 * time.Millisecond
w.config.connection_timeout = 30 * time.Second w.config.connection_timeout = 30 * time.Second
w.config.keepalive_period = 0 * time.Second w.config.keepalive_period = 0 * time.Second
w.config.keepalive_probes = 3 w.config.keepalive_probes = 3
@ -63,6 +66,14 @@ func (w *IMAPWorker) handleConfigure(msg *types.Configure) error {
value, err) value, err)
} }
w.config.idle_timeout = val 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": case "reconnect-maxwait":
val, err := time.ParseDuration(value) val, err := time.ParseDuration(value)
if err != nil || val < 0 { if err != nil || val < 0 {

View file

@ -25,6 +25,7 @@ type idler struct {
config imapConfig config imapConfig
client *imapClient client *imapClient
worker *types.Worker worker *types.Worker
last time.Time
stop chan struct{} stop chan struct{}
done chan error done chan error
waiting bool waiting bool
@ -63,20 +64,31 @@ func (i *idler) isReady() bool {
func (i *idler) Start() { func (i *idler) Start() {
if i.isReady() { if i.isReady() {
i.stop = make(chan struct{}) i.stop = make(chan struct{})
go func() { go func() {
defer logging.PanicHandler() defer logging.PanicHandler()
i.idleing = true select {
i.log("=>(idle)") case <-i.stop:
now := time.Now() // debounce idle
err := i.client.Idle(i.stop, i.log("=>(idle) [debounce]")
&client.IdleOptions{ i.done <- nil
LogoutTimeout: 0, case <-time.After(i.config.idle_debounce):
PollInterval: 0, // enter idle mode
}) i.idleing = true
i.idleing = false i.log("=>(idle)")
i.done <- err now := time.Now()
i.log("elapsed ideling time:", time.Since(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() { } else if i.isWaiting() {
i.log("not started: wait for idle to exit") i.log("not started: wait for idle to exit")
} else { } else {

View file

@ -43,6 +43,7 @@ type imapConfig struct {
folders []string folders []string
oauthBearer lib.OAuthBearer oauthBearer lib.OAuthBearer
idle_timeout time.Duration idle_timeout time.Duration
idle_debounce time.Duration
reconnect_maxwait time.Duration reconnect_maxwait time.Duration
// tcp connection parameters // tcp connection parameters
connection_timeout time.Duration connection_timeout time.Duration