From 9eea26459a79ef76ecb84a49205ba34d11efc905 Mon Sep 17 00:00:00 2001 From: Leander Beernaert Date: Wed, 18 Oct 2023 14:29:27 +0200 Subject: [PATCH] fix(GODT-3033): Unable to receive new mail If the IMAP service happened to finish syncing and wanted to reset the user event service at a time the latter was publishing an event a deadlock would occur and the user would not receive any new messages. This change puts the request to revert the event id in a separate go-routine to avoid this situation from re-occurring. The operational flow remains unchanged as the event service will only process this request once the current set of events have been published. --- internal/services/imapservice/service.go | 42 ++++++++++++++---------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/internal/services/imapservice/service.go b/internal/services/imapservice/service.go index fc178d95..293b86e1 100644 --- a/internal/services/imapservice/service.go +++ b/internal/services/imapservice/service.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "path/filepath" + "sync/atomic" "time" "github.com/ProtonMail/gluon/async" @@ -94,7 +95,7 @@ type Service struct { syncConfigPath string lastHandledEventID string - isSyncing bool + isSyncing atomic.Bool } func NewService( @@ -389,23 +390,28 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo continue } + // Start a goroutine to wait on event reset as it is possible that the sync received message + // was processed during an event publish. This in turn will block the imap service, since the + // event service is unable to reply to the request until the events have been processed. s.log.Info("Sync complete, starting API event stream") - if err := s.eventProvider.RewindEventID(ctx, s.lastHandledEventID); err != nil { - if errors.Is(err, context.Canceled) { - continue + go func() { + if err := s.eventProvider.RewindEventID(ctx, s.lastHandledEventID); err != nil { + if errors.Is(err, context.Canceled) { + return + } + + s.log.WithError(err).Error("Failed to rewind event service") + s.eventPublisher.PublishEvent(ctx, events.UserBadEvent{ + UserID: s.identityState.UserID(), + OldEventID: "", + NewEventID: "", + EventInfo: "", + Error: fmt.Errorf("failed to rewind event loop: %w", err), + }) } - s.log.WithError(err).Error("Failed to rewind event service") - s.eventPublisher.PublishEvent(ctx, events.UserBadEvent{ - UserID: s.identityState.UserID(), - OldEventID: "", - NewEventID: "", - EventInfo: "", - Error: fmt.Errorf("failed to rewind event loop: %w", err), - }) - } - - s.isSyncing = false + s.isSyncing.Store(false) + }() } case request, ok := <-s.syncUpdateApplier.requestCh: @@ -427,7 +433,7 @@ func (s *Service) run(ctx context.Context) { //nolint gocyclo continue } e.Consume(func(event proton.Event) error { - if s.isSyncing { + if s.isSyncing.Load() { if err := syncEventHandler.OnEvent(ctx, event); err != nil { return err } @@ -610,13 +616,13 @@ func (s *Service) setShowAllMail(v bool) { } func (s *Service) startSyncing() { - s.isSyncing = true + s.isSyncing.Store(true) s.syncHandler.Execute(s.syncReporter, s.labels.GetLabelMap(), s.syncUpdateApplier, s.syncMessageBuilder, syncservice.DefaultRetryCoolDown) } func (s *Service) cancelSync() { s.syncHandler.CancelAndWait() - s.isSyncing = false + s.isSyncing.Store(false) } type resyncReq struct{}