diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-25 12:46:31 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-25 12:46:31 +0200 |
| commit | b1db3e878a6fb468dc2c572c7080c572e3bd1ac5 (patch) | |
| tree | 181d2207b4810874afd65a7230e2e73a93a99291 /src/notmuchworker.cpp | |
| parent | 8b26c3e83a0328adb3f24d808b629fb73250d377 (diff) | |
| download | qtmaildir-b1db3e878a6fb468dc2c572c7080c572e3bd1ac5.tar.gz qtmaildir-b1db3e878a6fb468dc2c572c7080c572e3bd1ac5.zip | |
fix(worker): reopen the notmuch handle so queries see new mail
A read-only notmuch handle is a Xapian snapshot taken when it is opened,
so it never observes a write made by another process afterwards. The
worker opened one handle and kept it for the process lifetime, which made
the sync script's `notmuch new` invisible: every query after startup was
answered from the index as it stood when the application launched.
The symptom was mail arriving while the window was open and not appearing
until a restart. It was not confined to the post-sync refresh, which is
what made it hard to place: a query typed by hand also found nothing,
since it hits the same handle. Tag writes were unaffected throughout,
because applyTags opens its own read-write handle per call.
Reopen in openReadOnly() rather than at each call site: every read path
begins by asking for the handle. A reopen failure is deliberately not
fatal, since the existing handle is still usable and answering from a
slightly stale index beats refusing to answer.
The suite could not reproduce this before: the test helper builds a fresh
worker per query, so it opens a fresh handle every time. The new test
holds one worker across two queries and indexes between them from a
second process.
Item 104.
Diffstat (limited to 'src/notmuchworker.cpp')
| -rw-r--r-- | src/notmuchworker.cpp | 22 |
1 files changed, 21 insertions, 1 deletions
diff --git a/src/notmuchworker.cpp b/src/notmuchworker.cpp index f510fd5..f3a76ea 100644 --- a/src/notmuchworker.cpp +++ b/src/notmuchworker.cpp @@ -288,8 +288,28 @@ QByteArray NotmuchWorker::configPathArg() const bool NotmuchWorker::openReadOnly() { - if (m_db) + if (m_db) { + // A read-only handle is a Xapian SNAPSHOT taken when it was opened, so + // it never observes a write made by another process afterwards. The + // sync script's `notmuch new` is exactly that, which made mail arriving + // while the window was open invisible until the application restarted: + // not only to the post-sync refresh, but to any query the user typed by + // hand, since all of them are answered from the same handle. Item 104. + // + // Reopening here rather than at each call site covers every read path, + // which all begin by asking for the handle. It is cheap and it is what + // notmuch provides the call for; a failure is deliberately NOT fatal, + // since the existing handle is still usable and serving slightly stale + // results beats refusing to answer at all. + const notmuch_status_t status = + notmuch_database_reopen(m_db, NOTMUCH_DATABASE_MODE_READ_ONLY); + if (status != NOTMUCH_STATUS_SUCCESS) { + emit errorOccurred( + QStringLiteral("Cannot refresh notmuch database: %1") + .arg(QString::fromUtf8(notmuch_status_to_string(status)))); + } return true; + } const QByteArray configPath = configPathArg(); char *error = nullptr; |
