-
Notifications
You must be signed in to change notification settings - Fork 514
TSDB feature: Added FDB_TSDB_SEQ_MODE compile-time option to enable sequential timestamp mode, reducing flash usage. #388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…educe Flash usage in fixed-size blob scenarios. When enabled, removes log_len and log_addr fields from log_idx_data structure, saving 8 bytes per TSL entry. The log address is instead calculated at runtime based on the TSL's position within the sector. This optimization is ideal for applications logging fixed-size data (e.g., single temperature as float + timestamp) where the 8-byte overhead per entry becomes significant with large numbers of entries. Changes: - Conditionally exclude log_len/log_addr from log_idx_data struct - Calculate log address dynamically in read_tsl() when enabled - Add strict size validation in tsl_append() - Maintain backward compatibility via conditional compilation
…equential timestamp mode, reducing flash usage. This mode eliminates per-entry timestamp storage by calculating timestamps on-the-fly, saving sizeof(fdb_time_t) bytes per TSL entry (4 bytes for uint32_t, 8 bytes for uint64_t) per TSL entry. Key changes: - Removes field from structure when FDB_TSDB_SEQ_MODE is defined - Timestamps are calculated as: sector.start_time + TSL_index_in_sector - In tsl_append(), time auto-increments as db->last_time + 1 (no get_time() call needed) - In read_tsl(), timestamp is computed from sector start time and TSL position - Iterators pass sector.start_time via tsl.time before calling read_tsl() - search_start_tsl_addr() receives sector_start_time as additional parameter in SEQ_MODE Trade-offs: - Constraint: Timestamps must be monotonically increasing by 1 Depends on armink#387
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a compile-time sequential timestamp mode (FDB_TSDB_USING_SEQ_MODE) for the Time Series Database (TSDB) feature to reduce flash storage requirements. The mode eliminates per-entry timestamp storage by calculating timestamps on-the-fly based on sector start time and TSL index position, saving sizeof(fdb_time_t) bytes per entry (4-8 bytes depending on configuration).
Key changes:
- Conditionally removes the
timefield fromlog_idx_datastructure andget_timefunction pointer fromfdb_tsdbstructure when sequential mode is enabled - Implements timestamp calculation as
sector.start_time + TSL_index_in_sectorin read operations - Auto-increments timestamps as
db->last_time + 1in append operations without requiring time function calls - Updates all TSL iterators and search functions to pass sector start time for timestamp calculation
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 18 comments.
| File | Description |
|---|---|
| inc/fdb_cfg_template.h | Adds configuration template and documentation for the new FDB_TSDB_USING_SEQ_MODE and FDB_TSDB_FIXED_BLOB_SIZE compile-time options |
| inc/fdb_def.h | Conditionally removes get_time function pointer from fdb_tsdb structure when sequential mode is enabled |
| src/fdb_tsdb.c | Core implementation of sequential timestamp mode including modified read/write functions, timestamp calculation logic, iterator updates, and conditional compilation throughout |
Comments suppressed due to low confidence (1)
src/fdb_tsdb.c:464
- In FDB_TSDB_USING_SEQ_MODE, cur_time is always set to db->last_time + 1, which means the timestamp validation check on line 460 (cur_time <= db->last_time) will always evaluate to false and pass. This check is redundant in sequential mode and should be conditionally compiled out to avoid unnecessary overhead and misleading code. Consider wrapping the timestamp validation check (lines 460-464) with #ifndef FDB_TSDB_USING_SEQ_MODE to skip it when sequential mode is enabled.
/* check the current timestamp, MUST more than the last save timestamp */
if (cur_time <= db->last_time) {
FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than or equal to the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\n",
(intmax_t )cur_time, (intmax_t )(db->last_time));
return FDB_WRITE_ERR;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/fdb_tsdb.c
Outdated
| uint32_t idx_addr = db->cur_sec.empty_idx; | ||
|
|
||
| #ifdef FDB_TSDB_FIXED_BLOB_SIZE | ||
| uint32_t log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(FDB_TSDB_FIXED_BLOB_SIZE); |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this line uses tabs while the surrounding code uses spaces. This should be aligned with the project's indentation style.
| uint32_t log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(FDB_TSDB_FIXED_BLOB_SIZE); | |
| uint32_t log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(FDB_TSDB_FIXED_BLOB_SIZE); |
| #ifndef FDB_TSDB_FIXED_BLOB_SIZE | ||
| FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.log_len, sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET, false); | ||
| #endif | ||
| // #else no other index info |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this line uses a leading space followed by a tab, while the project appears to use consistent spacing. This should be aligned with the project's indentation style.
| // #else no other index info | |
| // #else no other index info |
src/fdb_tsdb.c
Outdated
| /* search all TSL */ | ||
| do { | ||
| #ifdef FDB_TSDB_USING_SEQ_MODE | ||
| tsl.time = sector.start_time; |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this line uses tabs while the surrounding code uses spaces. This should be aligned with the project's indentation style.
| tsl.time = sector.start_time; | |
| tsl.time = sector.start_time; |
src/fdb_tsdb.c
Outdated
| #if defined(FDB_TSDB_FIXED_BLOB_SIZE) || defined(FDB_TSDB_USING_SEQ_MODE) | ||
| uint32_t tsl_index_in_sector; | ||
| uint32_t sector_addr; |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition here combines two independent features (FDB_TSDB_FIXED_BLOB_SIZE and FDB_TSDB_USING_SEQ_MODE) but the variable declarations are only needed for their respective features. The tsl_index_in_sector and sector_addr variables should be declared in separate conditional blocks to avoid unnecessarily declaring them when only one feature is enabled. Consider splitting this into two separate conditional blocks or using a logical OR if both features need these variables.
| #if defined(FDB_TSDB_FIXED_BLOB_SIZE) || defined(FDB_TSDB_USING_SEQ_MODE) | |
| uint32_t tsl_index_in_sector; | |
| uint32_t sector_addr; | |
| #ifdef FDB_TSDB_USING_SEQ_MODE | |
| uint32_t tsl_index_in_sector; | |
| #endif | |
| #if defined(FDB_TSDB_FIXED_BLOB_SIZE) || defined(FDB_TSDB_USING_SEQ_MODE) | |
| uint32_t sector_addr; |
| #ifdef FDB_TSDB_USING_SEQ_MODE | ||
| #define LOG_IDX_TS_OFFSET ((unsigned long)(&((struct log_idx_data *)0)->log_len)) | ||
| #else | ||
| #define LOG_IDX_TS_OFFSET ((unsigned long)(&((struct log_idx_data *)0)->time)) | ||
| #endif |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When FDB_TSDB_USING_SEQ_MODE is defined, LOG_IDX_TS_OFFSET is set to the offset of log_len field in the log_idx_data structure. However, the log_len field only exists when FDB_TSDB_FIXED_BLOB_SIZE is NOT defined (see lines 103-106). This means if both FDB_TSDB_USING_SEQ_MODE and FDB_TSDB_FIXED_BLOB_SIZE are defined together, LOG_IDX_TS_OFFSET would reference a non-existent field, leading to incorrect offset calculations and potential memory corruption. Consider adding a compile-time check to prevent these two options from being enabled simultaneously, or adjust the logic to handle this combination properly.
| #ifndef FDB_TSDB_FIXED_BLOB_SIZE | ||
| FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.log_len, sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET, false); | ||
| #endif | ||
| // #else no other index info |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment "// #else no other index info" is confusing and appears to be a note that shouldn't be in the final code. If the intent is to document that no flash write is needed when both FDB_TSDB_USING_SEQ_MODE and FDB_TSDB_FIXED_BLOB_SIZE are defined, this should be expressed as a proper comment or removed entirely. Consider either removing this line or replacing it with a clearer comment like "/* No other index info to write when both SEQ_MODE and FIXED_BLOB_SIZE are enabled */".
| // #else no other index info | |
| /* No other index info to write when both SEQ_MODE and FIXED_BLOB_SIZE are enabled */ |
inc/fdb_cfg_template.h
Outdated
| /* #define FDB_TSDB_FIXED_BLOB_SIZE 4 */ | ||
|
|
||
|
|
||
| /* Use sequentual mode - eliminates per-entry timestamp storage by calculating timestamps |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling error: "sequentual" should be "sequential".
| /* Use sequentual mode - eliminates per-entry timestamp storage by calculating timestamps | |
| /* Use sequential mode - eliminates per-entry timestamp storage by calculating timestamps |
inc/fdb_cfg_template.h
Outdated
| /* Use sequentual mode - eliminates per-entry timestamp storage by calculating timestamps | ||
| * on-the-fly, saving sizeof(fdb_time_t). In this mode timestamps are monotonically increasing by 1 on each log | ||
| * and don't require application to provide a time function. |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment claims this saves "sizeof(fdb_time_t)" but the actual savings depends on whether FDB_TSDB_FIXED_BLOB_SIZE is also defined. Without FDB_TSDB_FIXED_BLOB_SIZE defined, the structure still contains log_len and log_addr fields. The comment should clarify that the savings apply to the timestamp field specifically, but the overall structure size reduction depends on other compile-time options.
| /* Use sequentual mode - eliminates per-entry timestamp storage by calculating timestamps | |
| * on-the-fly, saving sizeof(fdb_time_t). In this mode timestamps are monotonically increasing by 1 on each log | |
| * and don't require application to provide a time function. | |
| /* Use sequential mode - eliminates per-entry timestamp storage by calculating timestamps | |
| * on-the-fly, saving the space used for the timestamp field (sizeof(fdb_time_t)) per entry. | |
| * Note: The overall structure size reduction depends on other compile-time options (e.g., FDB_TSDB_FIXED_BLOB_SIZE). | |
| * In this mode, timestamps are monotonically increasing by 1 on each log and don't require the application to provide a time function. |
src/fdb_tsdb.c
Outdated
| fdb_time_t cur_time = db->last_time + 1; | ||
| (void)timestamp; | ||
| #else | ||
| fdb_time_t cur_time = timestamp == NULL ? db->get_time() : *timestamp; | ||
| #endif | ||
|
|
||
| #ifdef FDB_TSDB_FIXED_BLOB_SIZE | ||
| if(blob->size != FDB_TSDB_FIXED_BLOB_SIZE) { | ||
| FDB_INFO("Error: blob size (%zu) must equal FDB_TSDB_FIXED_BLOB_SIZE (%d)\n", blob->size, FDB_TSDB_FIXED_BLOB_SIZE); | ||
| return FDB_WRITE_ERR; | ||
| } |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this line uses tabs while the surrounding code uses spaces. This should be aligned with the project's indentation style.
| fdb_time_t cur_time = db->last_time + 1; | |
| (void)timestamp; | |
| #else | |
| fdb_time_t cur_time = timestamp == NULL ? db->get_time() : *timestamp; | |
| #endif | |
| #ifdef FDB_TSDB_FIXED_BLOB_SIZE | |
| if(blob->size != FDB_TSDB_FIXED_BLOB_SIZE) { | |
| FDB_INFO("Error: blob size (%zu) must equal FDB_TSDB_FIXED_BLOB_SIZE (%d)\n", blob->size, FDB_TSDB_FIXED_BLOB_SIZE); | |
| return FDB_WRITE_ERR; | |
| } | |
| fdb_time_t cur_time = db->last_time + 1; | |
| (void)timestamp; | |
| #else | |
| fdb_time_t cur_time = timestamp == NULL ? db->get_time() : *timestamp; | |
| #endif | |
| #ifdef FDB_TSDB_FIXED_BLOB_SIZE | |
| if(blob->size != FDB_TSDB_FIXED_BLOB_SIZE) { | |
| FDB_INFO("Error: blob size (%zu) must equal FDB_TSDB_FIXED_BLOB_SIZE (%d)\n", blob->size, FDB_TSDB_FIXED_BLOB_SIZE); | |
| return FDB_WRITE_ERR; | |
| } |
src/fdb_tsdb.c
Outdated
| if (from > to) { | ||
| tsl.addr.index = start; | ||
| #ifdef FDB_TSDB_USING_SEQ_MODE | ||
| tsl.time = start_time; |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this line uses tabs while the surrounding code uses spaces. This should be aligned with the project's indentation style.
| tsl.time = start_time; | |
| tsl.time = start_time; |
…ash data format, as requested in PR review
This regression was introduced by previous commit 0d3899a.
|
@EzraZigenbine can you fix the conflicts? |
|
fixed conflicts |
| fdb_time_t last_time; /**< last TSL timestamp */ | ||
| #ifndef FDB_TSDB_USING_SEQ_MODE | ||
| fdb_get_time get_time; /**< the current timestamp get function */ | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sequential mode, isn't that simpler?
- Do not allow users to set get_time functions from the outside, the system forces the default
get_timefunction, and users will be prompted with an error when setting it typedef fdb_time_t (*fdb_get_time)(void);->typedef fdb_time_t (*fdb_get_time)(fdb_tsdb_t db);- The default
get_timefunction of the system only needs to implementdb->last_time +1and then return this time number
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since FDB_TSDB_USING_SEQ_MODE is a compile-time option, my intention was to prevent misconfiguration at compile time rather than failing at runtime.
While compiling out get_time from the struct enforces this internally, I should also update the function signature to produce a compile-time error if an application attempts to pass a get_time function in SEQ mode, e.g.:
#ifdef FDB_TSDB_USING_SEQ_MODE
fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, size_t max_len, void *user_data);
#else
fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, db_get_time get_time, size_t max_len, void *user_data);
#endif
Also should have compiled out fdb_tsl_append_with_ts for sequential mode as it should never be called.
Enforcing this at runtime would be simpler, but it can result in delayed detection of misconfiguration.
Alternatively can see advantage of keeping consistent API and db struct. (simplifies code and maintenance). and error when setting get_time is more consistent with how fix blob work i.e. get run time error if not expected fixed size.
ok to update ether as suggested or add more compile time gards. let me know.
in above suggestion would this change:
typedef fdb_time_t (*fdb_get_time)(void); -> typedef fdb_time_t (*fdb_get_time)(fdb_tsdb_t db);
be for all builds or just seq mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Or you can do this, fdb_tsdb_init when initializing, the get_time parameter is empty, then the sequential mode is used by default, so that the initialization document needs to be updated clearly
- fdb_tsl_append_with_ts Make assertions in this sequential mode
- A v2.2 version can be released later, just explain the incompatibility clearly, fdb_get_time it is still necessary to modify, including fdb_tsl_cb I hope to change it as well to add fdb parameters

This mode eliminates per-entry timestamp storage by calculating timestamps
on-the-fly, saving sizeof(fdb_time_t) bytes per TSL entry (4 bytes for uint32_t, 8 bytes for uint64_t) per TSL entry.
Key changes:
Trade-offs:
Depends on #387