monitor runs in a separate goroutine and monitors the database & WAL. Implements exponential backoff on repeated sync errors to prevent disk churn when persistent errors (like disk full) occur. See issue #927.
()
| 2671 | // Implements exponential backoff on repeated sync errors to prevent disk churn |
| 2672 | // when persistent errors (like disk full) occur. See issue #927. |
| 2673 | func (db *DB) monitor() { |
| 2674 | ticker := time.NewTicker(db.MonitorInterval) |
| 2675 | defer ticker.Stop() |
| 2676 | |
| 2677 | // Backoff state for error handling. |
| 2678 | var backoff time.Duration |
| 2679 | var lastLogTime time.Time |
| 2680 | var consecutiveErrs int |
| 2681 | |
| 2682 | for { |
| 2683 | // Wait for ticker or context close. |
| 2684 | select { |
| 2685 | case <-db.ctx.Done(): |
| 2686 | return |
| 2687 | case <-ticker.C: |
| 2688 | } |
| 2689 | |
| 2690 | // If in backoff mode, wait additional time before retrying. |
| 2691 | if backoff > 0 { |
| 2692 | select { |
| 2693 | case <-db.ctx.Done(): |
| 2694 | return |
| 2695 | case <-time.After(backoff): |
| 2696 | } |
| 2697 | } |
| 2698 | |
| 2699 | // Sync the database to the shadow WAL. |
| 2700 | if err := db.Sync(db.ctx); err != nil && !errors.Is(err, context.Canceled) { |
| 2701 | consecutiveErrs++ |
| 2702 | |
| 2703 | // Exponential backoff: MonitorInterval -> 2x -> 4x -> ... -> max |
| 2704 | if backoff == 0 { |
| 2705 | backoff = db.MonitorInterval |
| 2706 | } else { |
| 2707 | backoff *= 2 |
| 2708 | if backoff > DefaultSyncBackoffMax { |
| 2709 | backoff = DefaultSyncBackoffMax |
| 2710 | } |
| 2711 | } |
| 2712 | |
| 2713 | // Log with rate limiting to avoid log spam during persistent errors. |
| 2714 | if time.Since(lastLogTime) >= SyncErrorLogInterval { |
| 2715 | db.Logger.Error("sync error", |
| 2716 | "error", err, |
| 2717 | "consecutive_errors", consecutiveErrs, |
| 2718 | "backoff", backoff) |
| 2719 | lastLogTime = time.Now() |
| 2720 | } |
| 2721 | |
| 2722 | // Try to clean up stale temp files after persistent disk errors. |
| 2723 | if isDiskFullError(err) && consecutiveErrs >= 3 { |
| 2724 | db.Logger.Warn("attempting temp file cleanup due to persistent disk errors") |
| 2725 | if cleanupErr := removeTmpFiles(db.metaPath); cleanupErr != nil { |
| 2726 | db.Logger.Error("temp file cleanup failed", "error", cleanupErr) |
| 2727 | } |
| 2728 | } |
| 2729 | continue |
| 2730 | } |
no test coverage detected