Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

FDS read order (fds_record_find) doesn't reflect write order (fds_record_write) for multiple records with same record_key and file_id, if virtual page boundaries are crossed

The assumption that `fds_record_find` returns records for NOT CHANGING file_id and record_key in the same
order they have been written IS WRONG.

The documentation sub-section "storage format" of FDS states the following:

```
Record layout

Records consist of a header (the record metadata) and the actual content. They are stored contiguously in flash in
the order they are written.
```

It turns out that this rule doesn't apply if virtual page boundaries are crossed.
The behavior seems to be the following:

If a record should be written but a virtual page couldn't take it (because the virtual page has too few space left)
it is written to the next virtual page with enough remaining space. If a successive operation tries to write a
smaller record, which still fits into the virtual page which couldn't be used for the first write (the last record
was too large, but this one fits) this record is written to the virtual page preceding the one used for the first
fds_record_write call. From fsstorage perspective, this means that these two records aren't layed out in write order,
because they are swapped.

Now the `fds_record_find` method follows a simple logic, if multiple records with same file_id and record_id should
be returned. It steps through each virtual page (in order) and for each virtual page it steps through all records and
returns the next record (according to the find_token), which matches the filter criteria (file_id and record_key).
For the example above, the iteration would hit the record which was written in the second call to `fds_record_write`
FIRST, because it was stored in the virtual page preceding the virtual page of the first call to fds_record_write.

This means if order of data is critical, different record_key (e.g. incrementing) or file_ids have to be used.
Records with same file_id and record_key have to be seen as "set" where read order (fds_record_find) isn't guaranteed
to reflect write order (successive calls to fds_record_write).

Note 1: Increasing virtual page size for FDS could mitigate the problem, but it still occurs if page boundaries are
crossed for records with same file_id and record_key.

Note 2: Not waiting for write operations to finish isn't part of the problem described here, as it was assured that
no successive `fds_record_write` calls have been enqueued, before the respective FDS_EVT_WRITE occurred.


The documentation of FDS isn't clear on the question if read order should align to write order (for records with
same record key and file ID), but at least that is the expected behavior IMO.

I can't provide stripped down sample code to reproduce it. The problem occured in one of my projects, source with affected
code could be found here:

https://github.com/mame82/LOGITacker/blob/master/logitacker/logitacker_script_engine.c#L554

--> `logitacker_script_engine_store_current_script_to_flash` stores multiple records with same record_key and file_id

where order is important (distinguishing header and data records)

--> `logitacker_script_engine_load_script_from_flash` reads back those record and relies on order (to distinguish header

records from data records)

In my case, the problem could be solved by using incremental record_keys. If this is an requirement, because multiple records

with same record_key and file_id not preserved order is intended, it should at least be pointed out in the documentation.

Related