From ffc7ed96bc493a1449164521c766c01c64b9c68d Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Sat, 1 Mar 2025 10:11:02 +0100 Subject: [PATCH] When delivering a message to a mailbox, remember last dir we delivered to In the common case, it's the same as the previous delivery. That means we don't have to try to create the directory (fewer syscalls) and that we can sync the dir to disk. This also tweaks the defer handling in case of a late failure. --- store/account.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/store/account.go b/store/account.go index 6aa12fd..046ebaa 100644 --- a/store/account.go +++ b/store/account.go @@ -873,6 +873,10 @@ type Account struct { // delivery, or aborting when importing. threadsErr error + // Message directory of last delivery. Used to check we don't have to make that + // directory when delivering. + lastMsgDir string + // Write lock must be held for account/mailbox modifications including message delivery. // Read lock for reading mailboxes/messages. // When making changes to mailboxes/messages, changes must be broadcasted before @@ -1655,7 +1659,7 @@ func (a *Account) WithRLock(fn func()) { // Caller must broadcast new message. // // Caller must update mailbox counts. -func (a *Account) DeliverMessage(log mlog.Log, tx *bstore.Tx, m *Message, msgFile *os.File, sync, notrain, nothreads, updateDiskUsage bool) error { +func (a *Account) DeliverMessage(log mlog.Log, tx *bstore.Tx, m *Message, msgFile *os.File, sync, notrain, nothreads, updateDiskUsage bool) (rerr error) { if m.Expunged { return fmt.Errorf("cannot deliver expunged message") } @@ -1812,7 +1816,13 @@ func (a *Account) DeliverMessage(log mlog.Log, tx *bstore.Tx, m *Message, msgFil msgPath := a.MessagePath(m.ID) msgDir := filepath.Dir(msgPath) - os.MkdirAll(msgDir, 0770) + if a.lastMsgDir != msgDir { + os.MkdirAll(msgDir, 0770) + if err := moxio.SyncDir(log, msgDir); err != nil { + return fmt.Errorf("sync message dir: %v", err) + } + a.lastMsgDir = msgDir + } // Sync file data to disk. if sync { @@ -1825,10 +1835,15 @@ func (a *Account) DeliverMessage(log mlog.Log, tx *bstore.Tx, m *Message, msgFil return fmt.Errorf("linking/copying message to new file: %w", err) } + defer func() { + if rerr != nil { + err := os.Remove(msgPath) + log.Check(err, "removing delivered message file", slog.String("path", msgPath)) + } + }() + if sync { if err := moxio.SyncDir(log, msgDir); err != nil { - xerr := os.Remove(msgPath) - log.Check(xerr, "removing message after syncdir error", slog.String("path", msgPath)) return fmt.Errorf("sync directory: %w", err) } } @@ -1836,8 +1851,6 @@ func (a *Account) DeliverMessage(log mlog.Log, tx *bstore.Tx, m *Message, msgFil if !notrain && m.NeedsTraining() { l := []Message{*m} if err := a.RetrainMessages(context.TODO(), log, tx, l, false); err != nil { - xerr := os.Remove(msgPath) - log.Check(xerr, "removing message after syncdir error", slog.String("path", msgPath)) return fmt.Errorf("training junkfilter: %w", err) } *m = l[0]