From 185786604b962cba55984ce861dd5000f30c72b9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 Feb 2026 09:48:32 -0600 Subject: [PATCH] [preferences] Replace per-element erase with clear() in sync() NVS/FDB write failures are permanent (flash worn out, partition full, handle invalid). The NVS layer already performs internal garbage collection during writes, so retrying the same call will always fail again. Keeping failed entries in the vector leaked memory and forced a reverse-iterate + per-element erase pattern that generated ~130 bytes of inlined vector move/destroy code for NVSData objects. Replace with a forward range-for and a single clear() at the end. --- esphome/components/esp32/preferences.cpp | 8 +++----- esphome/components/libretiny/preferences.cpp | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 7d5af023b4..8d6fdc86f6 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -124,14 +124,11 @@ class ESP32Preferences : public ESPPreferences { return true; ESP_LOGV(TAG, "Saving %zu items...", s_pending_save.size()); - // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; esp_err_t last_err = ESP_OK; uint32_t last_key = 0; - // go through vector from back to front (makes erase easier/more efficient) - for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) { - const auto &save = s_pending_save[i]; + for (const auto &save : s_pending_save) { char key_str[KEY_BUFFER_SIZE]; snprintf(key_str, sizeof(key_str), "%" PRIu32, save.key); ESP_LOGVV(TAG, "Checking if NVS data %s has changed", key_str); @@ -150,8 +147,9 @@ class ESP32Preferences : public ESPPreferences { ESP_LOGV(TAG, "NVS data not changed skipping %" PRIu32 " len=%zu", save.key, save.len); cached++; } - s_pending_save.erase(s_pending_save.begin() + i); } + s_pending_save.clear(); + ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, failed); if (failed > 0) { diff --git a/esphome/components/libretiny/preferences.cpp b/esphome/components/libretiny/preferences.cpp index 5a60b535da..8549631e46 100644 --- a/esphome/components/libretiny/preferences.cpp +++ b/esphome/components/libretiny/preferences.cpp @@ -114,14 +114,11 @@ class LibreTinyPreferences : public ESPPreferences { return true; ESP_LOGV(TAG, "Saving %zu items...", s_pending_save.size()); - // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; fdb_err_t last_err = FDB_NO_ERR; uint32_t last_key = 0; - // go through vector from back to front (makes erase easier/more efficient) - for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) { - const auto &save = s_pending_save[i]; + for (const auto &save : s_pending_save) { char key_str[KEY_BUFFER_SIZE]; snprintf(key_str, sizeof(key_str), "%" PRIu32, save.key); ESP_LOGVV(TAG, "Checking if FDB data %s has changed", key_str); @@ -141,8 +138,9 @@ class LibreTinyPreferences : public ESPPreferences { ESP_LOGD(TAG, "FDB data not changed; skipping %" PRIu32 " len=%zu", save.key, save.len); cached++; } - s_pending_save.erase(s_pending_save.begin() + i); } + s_pending_save.clear(); + ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, failed); if (failed > 0) {