mirror of
https://github.com/mjl-/mox.git
synced 2025-06-28 09:38:15 +03:00
check whether mailboxes have message/etc counts through an "upgrade" boolean flag
Instead of using the per-mailbox flag, and going through all mailboxes when opening an account.
This commit is contained in:
parent
b37faa06bd
commit
a68a9d8a48
2
ctl.go
2
ctl.go
@ -1522,7 +1522,7 @@ func servectlcmd(ctx context.Context, ctl *ctl, cid int64, shutdown func()) {
|
|||||||
}
|
}
|
||||||
totalSize += mc.Size
|
totalSize += mc.Size
|
||||||
|
|
||||||
if !mb.HaveCounts || mc != mb.MailboxCounts {
|
if mc != mb.MailboxCounts {
|
||||||
_, err := fmt.Fprintf(w, "for %s setting new counts %s (was %s)\n", mb.Name, mc, mb.MailboxCounts)
|
_, err := fmt.Fprintf(w, "for %s setting new counts %s (was %s)\n", mb.Name, mc, mb.MailboxCounts)
|
||||||
ctl.xcheck(err, "write")
|
ctl.xcheck(err, "write")
|
||||||
mb.HaveCounts = true
|
mb.HaveCounts = true
|
||||||
|
@ -221,7 +221,7 @@ type Mailbox struct {
|
|||||||
// lower case (for JMAP), sorted.
|
// lower case (for JMAP), sorted.
|
||||||
Keywords []string
|
Keywords []string
|
||||||
|
|
||||||
HaveCounts bool // Whether MailboxCounts have been initialized.
|
HaveCounts bool // Deprecated. Covered by Upgrade.MailboxCounts. No longer read.
|
||||||
MailboxCounts // Statistics about messages, kept up to date whenever a change happens.
|
MailboxCounts // Statistics about messages, kept up to date whenever a change happens.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,6 +949,7 @@ type Upgrade struct {
|
|||||||
Threads byte // 0: None, 1: Adding MessageID's completed, 2: Adding ThreadID's completed.
|
Threads byte // 0: None, 1: Adding MessageID's completed, 2: Adding ThreadID's completed.
|
||||||
MailboxModSeq bool // Whether mailboxes have been assigned modseqs.
|
MailboxModSeq bool // Whether mailboxes have been assigned modseqs.
|
||||||
MailboxParentID bool // Setting ParentID on mailboxes.
|
MailboxParentID bool // Setting ParentID on mailboxes.
|
||||||
|
MailboxCounts bool // Global flag about whether we have mailbox flags. Instead of previous per-mailbox boolean.
|
||||||
}
|
}
|
||||||
|
|
||||||
// upgradeInit is the value to for new account database, that don't need any upgrading.
|
// upgradeInit is the value to for new account database, that don't need any upgrading.
|
||||||
@ -957,6 +958,7 @@ var upgradeInit = Upgrade{
|
|||||||
Threads: 2,
|
Threads: 2,
|
||||||
MailboxModSeq: true,
|
MailboxModSeq: true,
|
||||||
MailboxParentID: true,
|
MailboxParentID: true,
|
||||||
|
MailboxCounts: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitialUIDValidity returns a UIDValidity used for initializing an account.
|
// InitialUIDValidity returns a UIDValidity used for initializing an account.
|
||||||
@ -1143,11 +1145,10 @@ func OpenAccountDB(log mlog.Log, accountDir, accountName string) (a *Account, re
|
|||||||
return acc, nil
|
return acc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure singletons are present. Mailbox counts and total message size, Settings.
|
// Ensure singletons are present, like DiskUsage and Settings.
|
||||||
// Process pending MessageErase records. Check that next the message ID assigned by
|
// Process pending MessageErase records. Check that next the message ID assigned by
|
||||||
// the database does not already have a file on disk, or increase the sequence so
|
// the database does not already have a file on disk, or increase the sequence so
|
||||||
// it doesn't.
|
// it doesn't.
|
||||||
var mentioned bool
|
|
||||||
err = db.Write(context.TODO(), func(tx *bstore.Tx) error {
|
err = db.Write(context.TODO(), func(tx *bstore.Tx) error {
|
||||||
if tx.Get(&Settings{ID: 1}) == bstore.ErrAbsent {
|
if tx.Get(&Settings{ID: 1}) == bstore.ErrAbsent {
|
||||||
if err := tx.Insert(&Settings{ID: 1, ShowAddressSecurity: true}); err != nil {
|
if err := tx.Insert(&Settings{ID: 1, ShowAddressSecurity: true}); err != nil {
|
||||||
@ -1155,23 +1156,6 @@ func OpenAccountDB(log mlog.Log, accountDir, accountName string) (a *Account, re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := bstore.QueryTx[Mailbox](tx).FilterEqual("HaveCounts", false).ForEach(func(mb Mailbox) error {
|
|
||||||
if !mentioned {
|
|
||||||
mentioned = true
|
|
||||||
log.Info("first calculation of mailbox counts for account", slog.String("account", accountName))
|
|
||||||
}
|
|
||||||
mc, err := mb.CalculateCounts(tx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mb.HaveCounts = true
|
|
||||||
mb.MailboxCounts = mc
|
|
||||||
return tx.Update(&mb)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
du := DiskUsage{ID: 1}
|
du := DiskUsage{ID: 1}
|
||||||
err = tx.Get(&du)
|
err = tx.Get(&du)
|
||||||
if err == bstore.ErrAbsent {
|
if err == bstore.ErrAbsent {
|
||||||
@ -1308,7 +1292,6 @@ func OpenAccountDB(log mlog.Log, accountDir, accountName string) (a *Account, re
|
|||||||
return nil, fmt.Errorf("calculating counts for mailbox, inserting settings, expunging messages: %v", err)
|
return nil, fmt.Errorf("calculating counts for mailbox, inserting settings, expunging messages: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start adding threading if needed.
|
|
||||||
up := Upgrade{ID: 1}
|
up := Upgrade{ID: 1}
|
||||||
err = db.Write(context.TODO(), func(tx *bstore.Tx) error {
|
err = db.Write(context.TODO(), func(tx *bstore.Tx) error {
|
||||||
err := tx.Get(&up)
|
err := tx.Get(&up)
|
||||||
@ -1458,6 +1441,34 @@ func OpenAccountDB(log mlog.Log, accountDir, accountName string) (a *Account, re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !up.MailboxCounts {
|
||||||
|
log.Debug("upgrade: ensuring all mailboxes have message counts")
|
||||||
|
|
||||||
|
err := acc.DB.Write(context.TODO(), func(tx *bstore.Tx) error {
|
||||||
|
err := bstore.QueryTx[Mailbox](tx).FilterEqual("HaveCounts", false).ForEach(func(mb Mailbox) error {
|
||||||
|
mc, err := mb.CalculateCounts(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mb.HaveCounts = true
|
||||||
|
mb.MailboxCounts = mc
|
||||||
|
return tx.Update(&mb)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
up.MailboxCounts = true
|
||||||
|
if err := tx.Update(&up); err != nil {
|
||||||
|
return fmt.Errorf("marking upgrade done: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("upgrade: ensuring message counts on all mailboxes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if up.Threads == 2 {
|
if up.Threads == 2 {
|
||||||
close(acc.threadsCompleted)
|
close(acc.threadsCompleted)
|
||||||
return acc, nil
|
return acc, nil
|
||||||
@ -1686,7 +1697,6 @@ func (a *Account) SetSkipMessageModSeqZeroCheck(skip bool) {
|
|||||||
//
|
//
|
||||||
// - Missing or unexpected on-disk message files.
|
// - Missing or unexpected on-disk message files.
|
||||||
// - Mismatch between message size and length of MsgPrefix and on-disk file.
|
// - Mismatch between message size and length of MsgPrefix and on-disk file.
|
||||||
// - Missing HaveCounts.
|
|
||||||
// - Incorrect mailbox counts.
|
// - Incorrect mailbox counts.
|
||||||
// - Incorrect total message size.
|
// - Incorrect total message size.
|
||||||
// - Message with UID >= mailbox uid next.
|
// - Message with UID >= mailbox uid next.
|
||||||
@ -1935,10 +1945,7 @@ func (a *Account) CheckConsistency() error {
|
|||||||
var totalMailboxSize int64
|
var totalMailboxSize int64
|
||||||
for _, mb := range mailboxNames {
|
for _, mb := range mailboxNames {
|
||||||
totalMailboxSize += mb.Size
|
totalMailboxSize += mb.Size
|
||||||
if !mb.HaveCounts {
|
if mb.MailboxCounts != counts[mb.ID] {
|
||||||
errmsg := fmt.Sprintf("mailbox %q (id %d) does not have counts, should be %#v", mb.Name, mb.ID, counts[mb.ID])
|
|
||||||
errmsgs = append(errmsgs, errmsg)
|
|
||||||
} else if mb.MailboxCounts != counts[mb.ID] {
|
|
||||||
mbcounterr := fmt.Sprintf("mailbox %q (id %d) has wrong counts %s, should be %s", mb.Name, mb.ID, mb.MailboxCounts, counts[mb.ID])
|
mbcounterr := fmt.Sprintf("mailbox %q (id %d) has wrong counts %s, should be %s", mb.Name, mb.ID, mb.MailboxCounts, counts[mb.ID])
|
||||||
errmsgs = append(errmsgs, mbcounterr)
|
errmsgs = append(errmsgs, mbcounterr)
|
||||||
}
|
}
|
||||||
|
@ -1695,7 +1695,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "HaveCounts",
|
"Name": "HaveCounts",
|
||||||
"Docs": "Whether MailboxCounts have been initialized.",
|
"Docs": "Deprecated. Covered by Upgrade.MailboxCounts. No longer read.",
|
||||||
"Typewords": [
|
"Typewords": [
|
||||||
"bool"
|
"bool"
|
||||||
]
|
]
|
||||||
|
@ -201,7 +201,7 @@ export interface Mailbox {
|
|||||||
Sent: boolean
|
Sent: boolean
|
||||||
Trash: boolean
|
Trash: boolean
|
||||||
Keywords?: string[] | null // Keywords as used in messages. Storing a non-system keyword for a message automatically adds it to this list. Used in the IMAP FLAGS response. Only "atoms" are allowed (IMAP syntax), keywords are case-insensitive, only stored in lower case (for JMAP), sorted.
|
Keywords?: string[] | null // Keywords as used in messages. Storing a non-system keyword for a message automatically adds it to this list. Used in the IMAP FLAGS response. Only "atoms" are allowed (IMAP syntax), keywords are case-insensitive, only stored in lower case (for JMAP), sorted.
|
||||||
HaveCounts: boolean // Whether MailboxCounts have been initialized.
|
HaveCounts: boolean // Deprecated. Covered by Upgrade.MailboxCounts. No longer read.
|
||||||
Total: number // Total number of messages, excluding \Deleted. For JMAP.
|
Total: number // Total number of messages, excluding \Deleted. For JMAP.
|
||||||
Deleted: number // Number of messages with \Deleted flag. Used for IMAP message count that includes messages with \Deleted.
|
Deleted: number // Number of messages with \Deleted flag. Used for IMAP message count that includes messages with \Deleted.
|
||||||
Unread: number // Messages without \Seen, excluding those with \Deleted, for JMAP.
|
Unread: number // Messages without \Seen, excluding those with \Deleted, for JMAP.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user