From 7b522de9e7931652840a0faab8b18509d9e917b8 Mon Sep 17 00:00:00 2001 From: Ihor Rusyn <0kaba0@gmail.com> Date: Sun, 19 Jul 2026 11:40:31 +0200 Subject: [PATCH] debug: log every loadOrInit stat outcome, not just the ErrNotExist branch (#644) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live evidence on latest (post-#655): the NextUID regression still reproduces, with only ONE createFresh call observed for the affected path (from yarilo-imap), none from yarilo-lmtp — disproving the "two processes both discover a missing file simultaneously" theory. yarilo-lmtp had already used the same folder successfully through uid=19 before yarilo-imap's os.Stat on the identical path returned ErrNotExist ~1.4s later. loadOrInit now logs the stat outcome (exists/size/mod_time, or the literal error) on every call, not just when it's about to call createFresh — so a live repro can see the full stat-outcome history for a given index_path across processes instead of inferring it from the presence/absence of a createFresh WARN on one side only. Purely additive logging — no behavior change. go test ./... green. --- helm/Chart.yaml | 4 ++-- internal/storage/index/file/folder.go | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index e4017805..bd433182 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: yarilo description: Production-grade IMAP/POP3/Submission mail server type: application -version: 2.0.23 -appVersion: "2.1.17" +version: 2.0.24 +appVersion: "2.1.18" keywords: - imap - submission diff --git a/internal/storage/index/file/folder.go b/internal/storage/index/file/folder.go index c8787f35..2b60699a 100644 --- a/internal/storage/index/file/folder.go +++ b/internal/storage/index/file/folder.go @@ -112,6 +112,19 @@ func (u *userIndex) OpenFolder(folder string, uidValidity uint32) (*mailbox.Fold // Caller holds no locks — this runs only once per session-folder. func (u *userIndex) loadOrInit(fs *folderState, uidValidity uint32) error { st, err := os.Stat(fs.indexPath) + // Breadcrumb (#644/#647): unconditional, every call — not just the + // ErrNotExist branch — so a live repro can see the FULL stat-outcome + // history for a given index_path across processes (e.g. lmtp seeing it + // exist at t0 and imap seeing ErrNotExist at t0+1.4s for the exact same + // path is otherwise only inferable from the absence of a second + // createFresh call, not directly observed). + if err != nil { + slog.Debug("fileindex: loadOrInit stat", "folder", fs.folder, + "index_path", fs.indexPath, "exists", false, "err", err.Error()) + } else { + slog.Debug("fileindex: loadOrInit stat", "folder", fs.folder, + "index_path", fs.indexPath, "exists", true, "size", st.Size(), "mod_time", st.ModTime().UnixNano()) + } switch { case errors.Is(err, os.ErrNotExist): return fs.createFresh(uidValidity)