Refactor how messages are added to mailboxes

DeliverMessage() is now MessageAdd(), and it takes a Mailbox object that it
modifies but doesn't write to the database (the caller must do it, and plenty
of times can do it more efficiently by doing it once for multiple messages).
The new AddOpts let the caller influence how many checks and how much of the
work MessageAdd() does. The zero-value AddOpts enable all checks and all the
work, but callers can take responsibility of some of the checks/work if it can
do it more efficiently itself.

This simplifies the code in most places, and makes it more efficient. The
checks to update per-mailbox keywords is a bit simpler too now.

We are also more careful to close the junk filter without saving it in case of
errors.

Still part of more upcoming changes.
This commit is contained in:
Mechiel Lukkien
2025-03-01 16:06:01 +01:00
parent 7855a32852
commit 2beb30cc20
13 changed files with 410 additions and 362 deletions

View File

@ -451,20 +451,15 @@ func (w Webmail) MessageCompose(ctx context.Context, m ComposeMessage, mailboxID
Size: xc.Size,
}
if ok, maxSize, err := acc.CanAddMessageSize(tx, nm.Size); err != nil {
xcheckf(ctx, err, "checking quota")
} else if !ok {
xcheckuserf(ctx, fmt.Errorf("account over maximum total message size %d", maxSize), "checking quota")
err = acc.MessageAdd(log, tx, &mb, &nm, dataFile, store.AddOpts{})
if err != nil && errors.Is(err, store.ErrOverQuota) {
xcheckuserf(ctx, err, "checking quota")
}
xcheckf(ctx, err, "storing message in mailbox")
// Update mailbox before delivery, which changes uidnext.
mb.Add(nm.MailboxCounts())
err = tx.Update(&mb)
xcheckf(ctx, err, "updating sent mailbox for counts")
err = acc.DeliverMessage(log, tx, &nm, dataFile, true, false, false, true)
xcheckf(ctx, err, "storing message in mailbox")
changes = append(changes, nm.ChangeAddUID(), mb.ChangeCounts())
})
@ -1027,6 +1022,16 @@ func (w Webmail) MessageSubmit(ctx context.Context, m SubmitMessage) {
panic(x)
}
}()
var deliveredIDs []int64
defer func() {
for _, id := range deliveredIDs {
p := acc.MessagePath(id)
err := os.Remove(p)
log.Check(err, "removing delivered message on error", slog.String("path", p))
}
}()
xdbwrite(ctx, acc, func(tx *bstore.Tx) {
if m.DraftMessageID > 0 {
nchanges := xops.MessageDeleteTx(ctx, log, tx, acc, []int64{m.DraftMessageID}, &modseq)
@ -1122,26 +1127,22 @@ func (w Webmail) MessageSubmit(ctx context.Context, m SubmitMessage) {
MsgPrefix: []byte(msgPrefix),
}
if ok, maxSize, err := acc.CanAddMessageSize(tx, sentm.Size); err != nil {
xcheckf(ctx, err, "checking quota")
} else if !ok {
xcheckuserf(ctx, fmt.Errorf("account over maximum total message size %d", maxSize), "checking quota")
}
// Update mailbox before delivery, which changes uidnext.
sentmb.Add(sentm.MailboxCounts())
err = tx.Update(&sentmb)
xcheckf(ctx, err, "updating sent mailbox for counts")
err = acc.DeliverMessage(log, tx, &sentm, dataFile, true, false, false, true)
if err != nil {
err = acc.MessageAdd(log, tx, &sentmb, &sentm, dataFile, store.AddOpts{})
if err != nil && errors.Is(err, store.ErrOverQuota) {
xcheckuserf(ctx, err, "checking quota")
} else if err != nil {
metricSubmission.WithLabelValues("storesenterror").Inc()
metricked = true
}
xcheckf(ctx, err, "message submitted to queue, appending message to Sent mailbox")
deliveredIDs = append(deliveredIDs, sentm.ID)
err = tx.Update(&sentmb)
xcheckf(ctx, err, "updating sent mailbox for counts")
changes = append(changes, sentm.ChangeAddUID(), sentmb.ChangeCounts())
})
deliveredIDs = nil
store.BroadcastChanges(acc, changes)
})
@ -1226,7 +1227,7 @@ func (Webmail) MailboxCreate(ctx context.Context, name string) {
xdbwrite(ctx, acc, func(tx *bstore.Tx) {
var exists bool
var err error
changes, _, exists, err = acc.MailboxCreate(tx, name, store.SpecialUse{})
_, changes, _, exists, err = acc.MailboxCreate(tx, name, store.SpecialUse{})
if exists {
xcheckuserf(ctx, errors.New("mailbox already exists"), "creating mailbox")
}

View File

@ -434,7 +434,7 @@ func TestView(t *testing.T) {
ChangeMailboxKeywords: store.ChangeMailboxKeywords{
MailboxID: inbox.ID,
MailboxName: inbox.Name,
Keywords: []string{`aaa`, `changelabel`},
Keywords: []string{`aaa`, `changelabel`, `testlabel`},
},
})
chmbcounts.Size = 0