when creating a mailbox subscription, don't just try to insert a record into the database and handle bstore.ErrUnique, the transaction will have been marked as botched

behaviour around failing DB calls that change data (insert/update) was changed
in bstore quite some time ago. the tx state in bstore would become inconsistent
when one or more (possibly unique) indexes had been modified, but then an
ErrUnique would occur for the next index. bstore doesn't know how to roll back
the partial changes during a transaction, so it marks the tx as botched and
refuses further operations. so, we cannot just try to insert, wait for a
possible ErrUnique, but then still try to continue with the transaction.
instead, we check if the record exists and only insert it if we couldn't find
it.

found while working on webmail.
This commit is contained in:
Mechiel Lukkien 2023-08-01 10:14:02 +02:00
parent 19550cc041
commit 141637df43
No known key found for this signature in database
3 changed files with 10 additions and 7 deletions

View file

@ -450,8 +450,8 @@ func importMessages(ctx context.Context, log *mlog.Log, token string, acc *store
err = tx.Insert(&mb) err = tx.Insert(&mb)
ximportcheckf(err, "inserting mailbox in database") ximportcheckf(err, "inserting mailbox in database")
err = tx.Insert(&store.Subscription{Name: p}) if tx.Get(&store.Subscription{Name: p}) != nil {
if err != nil && !errors.Is(err, bstore.ErrUnique) { err := tx.Insert(&store.Subscription{Name: p})
ximportcheckf(err, "subscribing to imported mailbox") ximportcheckf(err, "subscribing to imported mailbox")
} }
changes = append(changes, store.ChangeAddMailbox{Name: p, Flags: []string{`\Subscribed`}}) changes = append(changes, store.ChangeAddMailbox{Name: p, Flags: []string{`\Subscribed`}})

View file

@ -2470,8 +2470,9 @@ func (c *conn) cmdRename(tag, cmd string, p *parser) {
} }
err = tx.Insert(&mb) err = tx.Insert(&mb)
xcheckf(err, "creating parent mailbox") xcheckf(err, "creating parent mailbox")
err = tx.Insert(&store.Subscription{Name: parent})
if err != nil && !errors.Is(err, bstore.ErrUnique) { if tx.Get(&store.Subscription{Name: parent}) != nil {
err := tx.Insert(&store.Subscription{Name: parent})
xcheckf(err, "creating subscription") xcheckf(err, "creating subscription")
} }
changes = append(changes, store.ChangeAddMailbox{Name: parent, Flags: []string{`\Subscribed`}}) changes = append(changes, store.ChangeAddMailbox{Name: parent, Flags: []string{`\Subscribed`}})

View file

@ -970,10 +970,12 @@ func (a *Account) MailboxEnsure(tx *bstore.Tx, name string, subscribe bool) (mb
change := ChangeAddMailbox{Name: p} change := ChangeAddMailbox{Name: p}
if subscribe { if subscribe {
if tx.Get(&Subscription{p}) != nil {
err := tx.Insert(&Subscription{p}) err := tx.Insert(&Subscription{p})
if err != nil && !errors.Is(err, bstore.ErrUnique) { if err != nil {
return Mailbox{}, nil, fmt.Errorf("subscribing to mailbox: %v", err) return Mailbox{}, nil, fmt.Errorf("subscribing to mailbox: %v", err)
} }
}
change.Flags = []string{`\Subscribed`} change.Flags = []string{`\Subscribed`}
} }
changes = append(changes, change) changes = append(changes, change)