Skip to content

Conversation

@0x-fran
Copy link
Contributor

@0x-fran 0x-fran commented Jul 17, 2025

This PR introduces a RAM-based caching layer for configuration values stored in NVS, along with a batched flush mechanism to reduce latency and flash wear.

Summary of changes:

  • All Config::get*() calls now return cached values if available, avoiding repeated flash reads.
  • All Config::set*() calls only modify the RAM cache and mark entries as dirty.
  • A central flush_nvs_changes() function writes only dirty entries to NVS in a single transaction.
  • API endpoints now call flush_nvs_changes() after updates.
  • A background timer (every 5 minutes) flushes dirty values if needed.
  • System restart and OTA update paths also flush explicitly before rebooting.
  • Debug logging has been added to track cache hits (ESP_LOGD).

Benefits:

  • Significantly reduces flash read and write operations, especially during high-frequency polling (e.g. UI requests).
  • Minimizes blocking flash access in GET requests like /api/system/info.
  • Reduces flash wear and improves overall responsiveness.

No configuration values or behavior are affected unless they are written via the API or auto-flush is triggered.

@shufps
Copy link
Owner

shufps commented Jul 17, 2025

thx a lot!

The idea is good and I (mostly) like the implementation.

A couple of things I noticed on first glance:

  • it's using internal memory, maybe we could shift it to PSRAM and use the ALLOC/CALLOC macros for it. I'm always eagerly behind reducing internal memory where possible - this would work with PSRAM too imho
  • I think we don't need a periodic flusher - we know exactly when values change. It only can happen in the HTTP handler to store new values
  • It's not thread safe, imagine the cache being flushed but accessed through another thread concurrently

The config is a difficult topic at a whole I know. Complete mess 😅

I also tried - once at least - to refactor everything into a class with load and save and a member variable per field but it was a mess too.

I like the map approach though 🤔

What also would be possible is to cache the "rendered" json response from the HTTP API for a short period of time, it would reduce CPU load and NVS access too and would be maybe a little bit less "invasive".

Not sure though

@0x-fran
Copy link
Contributor Author

0x-fran commented Jul 18, 2025

Thanks for the quick feedback 🙌

  • Config cache now uses PSRAM via heap_caps_calloc() to save internal memory
  • All access is mutex-protected (simple FreeRTOS semaphore)
  • Removed periodic flusher; changes are flushed explicitly only when needed
  • Added optional short-lived JSON cache for /api/system/info (default: 15s)

PS: I added some ESP_LOGI lines (commented with TEMP: Debug) in system_handler.cpp for debugging the JSON caching – feel free to remove them if you'd prefer it clean.

@0x-fran 0x-fran changed the title Add RAM-based NVS caching and batched flush mechanism Add PSRAM-based NVS caching and batched flush mechanism Jul 18, 2025
@0x-fran 0x-fran changed the title Add PSRAM-based NVS caching and batched flush mechanism Add PSRAM-based NVS caching Jul 18, 2025
@0x-fran
Copy link
Contributor Author

0x-fran commented Jul 19, 2025

I realized the last commit missed a few key points 🙈
This commit improves upon the initial version by adding stricter NULL handling, correct PSRAM usage with heap_caps_strdup(), and splitting NVS flushing into a non-blocking working copy.

Summary:

  • handles potential NULL values more robustly (fallbacks always return valid strings)
  • all strings are now consistently allocated in PSRAM (heap_caps_strdup)
  • flush_nvs_changes() copies dirty entries into a temporary working buffer to avoid holding the mutex during I/O
  • dump_cache_status() added for optional diagnostics (currently unused)

@0x-fran
Copy link
Contributor Author

0x-fran commented Jul 19, 2025

Pushed too quickly earlier 🙈 Sorry 🙌
This commit fixes broken return types, ensures proper heap_caps_strdup() use for PSRAM allocation, and improves NULL handling in get_string. compiles now cleanly and behave as expected.

@0x-fran 0x-fran marked this pull request as draft July 20, 2025 07:36
@0x-fran 0x-fran force-pushed the perf/nvs-caching branch from 6ea02ec to fcdeed4 Compare July 22, 2025 14:37
@0x-fran
Copy link
Contributor Author

0x-fran commented Jul 22, 2025

🙌

@shufps : Pls. let me know if you’d prefer a new PR instead.

Changes:

  • removed JSON Caching due to weird behavior -> 1c65567
  • Enhanced thread-safety and added type mismatch checks in setters.
  • Improved error handling across all getters/setters (e.g., for PSRAM allocation).
  • Added detailed per-entry logging in dump_cache_status (incl. type information).
  • Optimized flush_nvs_changes with proper memory management for dirty entries

Sample logoutput:

₿ I (16671) nvs_config: Cache miss for string key: influx_org. Reading from NVS.
₿ I (16681) nvs_config: Cache miss for string key: influx_prefix. Reading from NVS.
₿ I (34121) nvs_config: Scanning for dirty entries to flush.
₿ I (34121) nvs_config: Found 25 dirty entries. Flushing to NVS...
₿ I (34141) nvs_config: Configuration changes flushed to NVS.
₿ I (34141) http_system: Configuration changes flushed to NVS

PS: Performance improvement using PSRAM cache about ~400x faster than NVS 🚀🥳

@0x-fran 0x-fran marked this pull request as ready for review July 22, 2025 14:55
@kukulle-hood
Copy link

Hi, is this subject still in the loop of attention?
Happy Sunday to all of you.

@0x-fran
Copy link
Contributor Author

0x-fran commented Oct 24, 2025

Rebased the branch onto latest develop and resolved all conflicts manually (including nvs_config.cpp).
Final adjustments were made to the NVS caching logic after testing.

The branch should now be ready for the next release @shufps 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants