GODT-2029: Handle deadlock when reordering user addresses

This commit is contained in:
James Houlahan
2022-11-09 16:21:05 +01:00
parent 04f2dd1a0b
commit 28d78453b7
2 changed files with 19 additions and 17 deletions

View File

@ -35,10 +35,6 @@ import (
// handleAPIEvent handles the given liteapi.Event. // handleAPIEvent handles the given liteapi.Event.
func (user *User) handleAPIEvent(ctx context.Context, event liteapi.Event) error { func (user *User) handleAPIEvent(ctx context.Context, event liteapi.Event) error {
ctx = logging.WithLogrusField(ctx, "eventID", event.EventID)
logging.LogFromContext(ctx).Info("Handling event")
if event.User != nil { if event.User != nil {
if err := user.handleUserEvent(ctx, *event.User); err != nil { if err := user.handleUserEvent(ctx, *event.User); err != nil {
return err return err
@ -77,7 +73,7 @@ func (user *User) handleUserEvent(_ context.Context, userEvent liteapi.User) err
user.apiUser = userEvent user.apiUser = userEvent
user.eventCh.Enqueue(events.UserChanged{ user.eventCh.Enqueue(events.UserChanged{
UserID: user.ID(), UserID: user.apiUser.ID,
}) })
return nil return nil
@ -135,17 +131,18 @@ func (user *User) handleCreateAddressEvent(ctx context.Context, event liteapi.Ad
user.updateCh[event.Address.ID] = queue.NewQueuedChannel[imap.Update](0, 0) user.updateCh[event.Address.ID] = queue.NewQueuedChannel[imap.Update](0, 0)
} }
user.eventCh.Enqueue(events.UserAddressCreated{
UserID: user.apiUser.ID,
AddressID: event.Address.ID,
Email: event.Address.Email,
})
return nil return nil
}, user.apiAddrsLock, user.updateChLock); err != nil { }, user.apiAddrsLock, user.updateChLock); err != nil {
return fmt.Errorf("failed to handle create address event: %w", err) return fmt.Errorf("failed to handle create address event: %w", err)
} }
user.eventCh.Enqueue(events.UserAddressCreated{ // Perform the sync in an RLock.
UserID: user.ID(),
AddressID: event.Address.ID,
Email: event.Address.Email,
})
return safe.RLockRet(func() error { return safe.RLockRet(func() error {
if user.vault.AddressMode() == vault.SplitMode { if user.vault.AddressMode() == vault.SplitMode {
if err := syncLabels(ctx, user.client, user.updateCh[event.Address.ID]); err != nil { if err := syncLabels(ctx, user.client, user.updateCh[event.Address.ID]); err != nil {
@ -171,7 +168,7 @@ func (user *User) handleUpdateAddressEvent(_ context.Context, event liteapi.Addr
user.apiAddrs[event.Address.ID] = event.Address user.apiAddrs[event.Address.ID] = event.Address
user.eventCh.Enqueue(events.UserAddressUpdated{ user.eventCh.Enqueue(events.UserAddressUpdated{
UserID: user.ID(), UserID: user.apiUser.ID,
AddressID: event.Address.ID, AddressID: event.Address.ID,
Email: event.Address.Email, Email: event.Address.Email,
}) })
@ -197,7 +194,7 @@ func (user *User) handleDeleteAddressEvent(_ context.Context, event liteapi.Addr
delete(user.apiAddrs, event.ID) delete(user.apiAddrs, event.ID)
user.eventCh.Enqueue(events.UserAddressDeleted{ user.eventCh.Enqueue(events.UserAddressDeleted{
UserID: user.ID(), UserID: user.apiUser.ID,
AddressID: event.ID, AddressID: event.ID,
Email: addr.Email, Email: addr.Email,
}) })
@ -248,7 +245,7 @@ func (user *User) handleCreateLabelEvent(_ context.Context, event liteapi.LabelE
} }
user.eventCh.Enqueue(events.UserLabelCreated{ user.eventCh.Enqueue(events.UserLabelCreated{
UserID: user.ID(), UserID: user.apiUser.ID,
LabelID: event.Label.ID, LabelID: event.Label.ID,
Name: event.Label.Name, Name: event.Label.Name,
}) })
@ -275,7 +272,7 @@ func (user *User) handleUpdateLabelEvent(_ context.Context, event liteapi.LabelE
} }
user.eventCh.Enqueue(events.UserLabelUpdated{ user.eventCh.Enqueue(events.UserLabelUpdated{
UserID: user.ID(), UserID: user.apiUser.ID,
LabelID: event.Label.ID, LabelID: event.Label.ID,
Name: event.Label.Name, Name: event.Label.Name,
}) })
@ -300,7 +297,7 @@ func (user *User) handleDeleteLabelEvent(_ context.Context, event liteapi.LabelE
} }
user.eventCh.Enqueue(events.UserLabelDeleted{ user.eventCh.Enqueue(events.UserLabelDeleted{
UserID: user.ID(), UserID: user.apiUser.ID,
LabelID: event.ID, LabelID: event.ID,
Name: label.Name, Name: label.Name,
}) })
@ -421,6 +418,11 @@ func (user *User) handleDeleteMessageEvent(ctx context.Context, event liteapi.Me
func (user *User) handleUpdateDraftEvent(ctx context.Context, event liteapi.MessageEvent) error { //nolint:unparam func (user *User) handleUpdateDraftEvent(ctx context.Context, event liteapi.MessageEvent) error { //nolint:unparam
return safe.RLockRet(func() error { return safe.RLockRet(func() error {
user.log.WithFields(logrus.Fields{
"messageID": event.ID,
"subject": logging.Sensitive(event.Message.Subject),
}).Info("Handling draft updated event")
full, err := user.client.GetFullMessage(ctx, event.Message.ID) full, err := user.client.GetFullMessage(ctx, event.Message.ID)
if err != nil { if err != nil {
return fmt.Errorf("failed to get full draft: %w", err) return fmt.Errorf("failed to get full draft: %w", err)

View File

@ -223,7 +223,7 @@ func New(
return return
} }
user.log.WithField("eventID", event.EventID).Debug("Updated event ID") user.log.WithField("eventID", event.EventID).Debug("Updated event ID in vault")
}) })
// When triggered, attempt to sync the user. // When triggered, attempt to sync the user.