Saving a Fast-Changing Counter to Flash: Choose What May Be Lost Before the Next Checkpoint
Choose a counter checkpoint policy from an explicit loss budget, then verify the resulting storage workload. “Save less often” is incomplete until you state how many accepted events may disappear after a sudden reset, how commit failures are handled, and whether the recovered total may move backward.
This article concerns a cumulative event count whose product owner permits bounded loss. It does not design unique sequence numbers, billing-grade metering, anti-replay state, or a counter that must never roll back. Those requirements need stronger system-level guarantees. Zephyr v4.2.0 NVS is used as a concrete storage interface; the loss model itself is independent of NVS.
1. Define what is being counted
Separate the physical event, its acceptance into RAM, the checkpoint snapshot, and durable checkpoint completion. A sensor pulse that never reaches the counting software is an acquisition problem. An accepted RAM event lost at reset is a persistence-policy problem. Keep those losses distinct in requirements and tests.
State the unit, maximum value, overflow behavior, and whether an event may be replayed after restart. A checkpoint can preserve a number perfectly while a replayed input is counted twice. Define which component owns replay suppression if it matters to the product.

2. Choose time-based or count-based checkpoints
A time-based policy snapshots the counter periodically. A count-based policy snapshots after a specified number of additional accepted events. The former gives a straightforward age target; the latter relates writes more directly to activity. Either needs a limit on the delay between its trigger and completed persistence.
| Policy | Useful when | Qualification question |
|---|---|---|
| Every T seconds while dirty | The requirement limits stale time and activity is variable. | What is the longest trigger jitter and commit duration? |
| Every K additional events | The requirement limits lost events and idle periods are long. | How many events arrive while the checkpoint is pending? |
| First of a time or count threshold | Both stale age and event loss matter. | Can triggers coalesce without extending either bound unexpectedly? |
| Shutdown-only save | A separately qualified shutdown path is guaranteed. | What happens during abrupt loss that bypasses the shutdown path? |
Do not treat a shutdown notification as guaranteed energy or execution time. If abrupt power loss is within scope, qualify the actual power and firmware behavior or retain a periodic checkpoint policy.
3. Calculate a defensible loss envelope
For a time policy, let T be the nominal snapshot interval, J the maximum additional scheduling delay, and D the maximum time from taking a snapshot to its confirmed durable completion. If all events obey an arrival envelope of at most B + r×t events in any interval of t seconds, a conservative loss allowance is B + ceiling(r×(T + J + D)). Round to whole events.
This is an engineering model, not a flash specification. It assumes snapshots continue on schedule, every checkpoint succeeds within D, accepted events are counted correctly, and the last completed checkpoint remains recoverable. A failed or indefinitely delayed checkpoint invalidates the bound. Define a fault state or stop accepting events before continuing beyond the allowed unpersisted budget.
For a count policy, the K events accumulated before the trigger are still vulnerable until the snapshot commits. Add the bounded arrivals during the trigger-to-completion interval. Do not promise a maximum loss of K merely because K is the save threshold.
4. Keep RAM updates independent of the pending snapshot
Take an atomic or appropriately locked snapshot, then allow new events to continue updating the live counter. Associate completion with the value actually submitted. Never clear all “dirty” state when a checkpoint completes if additional events arrived while it was in flight.
Record the last confirmed checkpoint value separately from the current RAM value. A failed write must not advance the confirmed value. Serialize checkpoint submissions or use an explicit ordering policy so that an older delayed completion cannot replace a newer acknowledged checkpoint in the application’s bookkeeping.
For NVS, check the v4.2.0 write return contract and the wrapper’s error handling. Repeatedly submitting an unchanged ID/data pair can avoid a physical write, according to the NVS documentation; a genuinely increasing counter normally changes the payload. Do not estimate its traffic from a test loop that repeatedly stores one constant.
5. Translate checkpoints into a write budget
Count application checkpoint requests, physical programmed bytes, and per-sector erases separately. Metadata, alignment, other live values, and reclaim copies can make their ratios differ. Measure the steady-state workload on disposable test storage with the intended layout and other writers active.
Compare the resulting erase distribution with the exact device’s endurance conditions, temperature requirements, and retention expectations. For example, the SST26VF064B/SST26VF064BA datasheet DS20005119K separates program operations from sector erases. A checkpoint is not automatically an erase cycle, and the busiest sector matters more than a partition-wide average.
Use this evidence to choose a checkpoint interval or storage technology. Do not turn an application-write count into a lifetime guarantee without the actual wear distribution and qualified device conditions.
Hypothetical worksheet
Suppose an event source is bounded by B = 10 and r = 40 events per second. A proposed timer uses T = 5 seconds, with J = 0.2 seconds and D = 0.3 seconds. Under the stated successful-checkpoint assumptions, the conservative allowance is 10 + ceiling(40×5.5), or 230 events per abrupt failure.
Continuous activity would trigger about 17,280 timer checkpoints per day at a five-second interval. That is an application workload estimate, not measured flash erases. If the product permits only 100 lost events, this proposal fails the requirement before any endurance calculation. Shorten the vulnerable interval, reduce delay, or change the persistence architecture.
6. Test reboot and failure cases
- Reset immediately before a trigger, after the snapshot, during persistence, and just after confirmed completion.
- Inject events during a pending checkpoint and verify they remain dirty afterward.
- Force write failure and verify the application reports its loss bound as unavailable rather than claiming a successful save.
- Test overflow, repeated reboots, duplicate input replay, and a missing or invalid checkpoint.
- Verify that recovery chooses a validated checkpoint and reports the permitted uncertainty honestly.
Release a counter-loss worksheet, checkpoint state trace, and measured storage workload together. The correct policy is the one whose loss envelope and wear evidence both satisfy the product’s requirements, including the failure path.