1. Тема дня
После crash consistency нужно посчитать ресурс Flash. Ресурс расходуют physical program/erase operations, а не размер переменной сам по себе. Главная мысль: запись 4 байт раз в секунду может превратиться в сотни миллионов logical updates за 10 лет.
2. Зачем это нужно
Опасные кандидаты:
mqtt_reconnect_count++
modem_reset_count++
uart_error_count++
phase_transition_count++
watchdog_warning_count++Если после каждого increment делать nvs_commit(), ресурс может закончиться намного раньше срока службы.
3. Теория
Endurance budget
Оценка:
N_updates ≈ E * P * Uгде:
E = erase cycles на страницу
P = число страниц wear levelling
U = logical updates между erase одной страницыNVS ESP-IDF
NVS хранит записи append-only. Новое значение дописывается, старое инвалидируется. Страница NVS 4096 байт, entry 32 байта, integer до 8 байт занимает одну entry. Для маленьких values NVS существенно снижает erase frequency, но ресурс всё равно конечен.
10-летний масштаб
1 commit/s -> 315 576 000 commits / 10 years
1 commit/10s -> 31 557 600
1 commit/min -> 5 259 600
1 commit/hour -> 87 660Durability classes
D0 volatile: можно потерять при reset
D1 approximate: можно потерять последние минуты
D2 checkpointed: можно потерять последние N операций
D3 immediately durable: ACK только после commitCounter batching
Runtime value меняется часто, Flash checkpoint редко:
commit если dirty >= 100 или age >= 60sCoalescing
Несколько быстрых config changes объединить в один snap- shot/commit.
Разные partitions
Разные write rates лучше разводить по разным NVS partitions:
nvs_factory read-only
nvs_config rare durable writes
nvs_stats periodic checkpoints4. Типичные ошибки
- «Всего 4 байта» = маленький износ.
- Путать program и erase cycles.
- nvs_commit() после каждого счётчика.
- Высокочастотную статистику держать рядом с критичной config.
- Использовать NVS как binary logger.
- Усреднённые 100k cycles вместо datasheet конкретной Flash.
- Огромный blob snapshot при изменении одного поля.
- Минимальная partition без места для GC.
- Откладывать commit D3-операции, уже подтверждённой backend.
- На STM32 перезаписывать одну Flash page как EEPROM.
- Не считать commits в soak/HIL.
5. Практическое задание
Сделай таблицу persistent objects:
name
updates/day
allowed data loss
D-class
storage backendНайди все записи:
rg "nvs_set_|nvs_commit|esp_partition_write|wl_write"Добавь storage diagnostics:
typedef struct {
uint64_t nvs_set_calls;
uint64_t nvs_commit_calls;
uint64_t bytes_requested;
uint64_t checkpoint_skips;
uint64_t checkpoint_writes;
} storage_write_stats_t;Переделай один счётчик, например mqtt_reconnect_count: RAM increment, commit если dirty >= 10 или age >= 60s. Проверь допустимую потерю после esp_restart().
6. Что попробовать дальше
- Добавить nvs_get_stats() в CLI.
- 1000 обновлений uint32_t vs 1000 обновлений 1024-byte blob — сравнить расход entries.
- Для STM32 заранее рассчитать required endurance до выбора Flash layout.
Задание
По упрощённой оценке N_updates ≈ E * P * U возьмите E=1000 erase cycles/page, P=4 pages, U=100 logical updates на erase страницы. Вычислите budget и объясните, почему это не измеренная гарантия lifetime конкретной Flash.
Критерии самопроверки: Получите 400000 и назовите assumptions устройства/workload вместо гарантированного lifetime.
Показать ответ автора
Оценка: 1000 * 4 * 100 = 400000 logical updates. Реальный ресурс зависит от datasheet конкретной Flash, layout storage, размеров updates, garbage collection и write amplification. Оценка требует проверки workload и endurance assumptions; это не hardware measurement.