tomazbracic
Storage on embedded (Nerves) device. Cubdb limits?
Hi,
I am building a Nerves firmware which will be used to collect some PMU (Phaser Measurement Unit) measurements and other necessary things like connectivity over LTE, storage, retrieval of data on request, etc.
Data will be collected at 50Hz rate/interval 24/7/365. This will not be small amount of data.
I would like to have 30 day data on a device. If that would be too problematic I would go for less, but this will be on a remote locations and pretty much decision was made without me ![]()
I will be using reComputer as hardware which is based on RPi CM4. Together with LTE module.
For storing data locally there are few options right now on my table and in my head.
-
CubDB. My first choice for my POC. I developed a logic to benchmark this so I am writing a lot of data in, my keys are timestamps since I will be query on that only for time ranges that I need to retrieve data later if issue detected. I can run my own queries, but currently i have like 2MIO records in it. To my calculations I might have like 110 - 150MIO records in there. Not sure (yet) about how it will behave. I’ve created a logic to delete data based on my retention, etc. For now kind of work just ok. Not sure yet about this compactions (everytime it changes the filename), not sure about behaviour on bigger number of records… don’t know it yet so I am not aware of its limits, gotchas…
-
Files like Parquet and handling things like “daily index” approach. Easy to drop older than 30 days, easy to query those not being used for writes, BUT hard/can’t access data from current file (open for write)
-
DuckDB? Really like the idea behind it, but Elixir client is not fully supported (yet). I would like partitioning though, but is not there.
-
Something else?
Does anyone have any experience with Cubdb and dealing with numbers around those above? Will it perform ok, are there issues to be expected? Would it be better to use SQLite? I am preparing myself a simulation environment, but I am not there yet. Final setup should be on a nerves device. So if anyone can share your own experiences or what to be careful about please I would really appreciate. Or if there is some other intersting approach that you used and it worked well for you I would really like to know about it.
Thanks,
Tomaz
Most Liked
lucaong
Hi @tomazbracic ,
author of CubDB here
while myself and others have used CubDB for many data logging tasks, your case may deserve special care. Most cases I am familiar with were either a slower continuous rate, or a higher one but in bursts, or a lower retention. In principle, I don’t see any hard limit, the write speed is not concerning, but due to the long retention and the continuous write, in practice, there are a couple of things to be mindful of.
The main such thing, as you correctly note, is compaction. Compaction progresses concurrently to read and writes, but can take a while for large files, and it will need to catch up with writes that happened while the compaction was ongoing. Since your data write is a continuous 50 writes per second, with no pause, there can be a lot to catch up with. Therefore, while it’s possible that everything would work just fine, compaction is one area where I would recommend doing some preliminary test. Since compaction only reclaims disk space, and does not affect read or write performance noticeably, if you have enough disk space you could disable auto compaction and perform it programmatically at specific chosen moments of downtime (if there is any).
Do you need to query old data live, or are you simply logging it? In the latter case, you could even disable auto compaction completely and start a new database in a new data directory each day, removing old databases and their data directories after the retention period.
In order to help you decide if CubDB is the right choice for you, I have a few other questions:
- How important is durability of data or atomicity of transactions in presence of sudden unexpected shutdowns? This is one of the situations that CubDB is designed to handle.
- Is it helpful to use native Elixir data structures in your data, or they could just be serialized strings? In the first case CubDB can help a lot, in the second you have more freedom on what to use to store data (SQLite for example).
- I assume the write pattern is that of a data logger (mostly only appends). What about your read pattern? Do you need to query your data live?
- Do you need the same granularity for all the retention period, or is it possible to summarize or coarse grain data older than, say, a few days?
lucaong
If you really only append new logs, and basically never need to query such logs live, I think that CubDB might not be needed. The best features of CubDB are atomic transactions, extreme robustness in face of unexpected shutdowns and power loss, possibility to use native Elixir data structures, and ease of performing both point queries and selection. In your case, as far as I can see, none of that is really needed. You do need durability, but given the append-only write pattern, you can achieve it in other ways.
In light of what you wrote, probably the easiest option is to simply write plain flat log files, one per day, using a naming scheme such as year-month-day.log, and perform log rotation by removing the old ones after the retention period. If your data is such that there is a simple way to separate each entry (say, one per line), and you really never need to query that data, this would work and be simple to maintain and understand. If you need durability, you will have to make sure to fsync each write (slow, but safe), or if it’s acceptable to loose, say, the last few seconds of data, fsync every N seconds or every N writes (more performant). Using a human readable format for your data would have the added benefit of making your logs readable just by opening them in a text editor, or with command line tools like awk (although you would probably need to load the logs into some system for deeper analysis).
If you instead need to query this data live, you could follow a hybrid approach, where you log raw un-aggregated data in plain log files like explained above, but also store aggregated data into a database for live querying. In a few project, for example, my system was allowing live querying such as “what’s the average value of X between last Tuesday 3pm and Saturday 4pm”, which would be served by CubDB but could use data aggregated by minute, and also allowed operators to access the device and download un-aggregated raw log files for offline investigation.
If you do need to query the data live, at arbitrary granularity, but typically only for the current day, you could do more or less the same as the first suggestion, but using CubDB instead of plain log files. You could restart CubDB on a different data directory at the beginning of each day. You can at that point disable compaction completely, which also ensures that the DB file never changes name, and remove old data directories after the retention period, like you would do with plain files. This way, you gain the possibility to query your data live, but on the other hand your logs cannot be read in a plain text editor. Alternatively, you can use SQLite, which is a great option, but would also very likely need some tweaking of the configuration to support the write pattern and performance that you need.
I hope this helps, happy to answer further questions if needed ![]()
lucaong
If you decide to use plain log files, note that the standard Elixir Logger with its default logger_std_h handler already supports log rotation, as well as delayed_write and filesync_repeat_interval options for tweaking durability and write performance (documented in the file module). Unfortunately, as far as I know, in Elixir and Erlang it is not generally possible to configure a special purpose logger distinct from the global logger.
It is possible to use filters to direct specific logs to a different handler based on level or metadata, but I find it obscures the intent a bit, when what you really want is to treat your data logs differently from all the rest.
One possibility then is to bypass logger and handlers completely and use directly the Erlang disk_log, which offers log rotation and other useful options. One limitation is that it rotates the files by size, not by date, which makes it more difficult to implement precisely the 30 days retention you want.
Last Post!
lucaong
Here’s another idea, mixing up suggestions from above, which might be a good solution if you log timestamped values (appending only), but also support live read access to data for a range of timestamps (e.g. “show me all values between timestamp A and B”).
You could use flat append-only log files, rotated each day or so and removed after 30 days, as described above. On top of that, to allow for querying a range of values, you could store in a database like CubDB or SQLite a reference to the file and offset every N log entries.
Your log files would contain all entries (possibly in a human-readable text format, and separated by a suitable separation, e.g. newline). The database would instead contain data indexed by timestamp, pointing at the file and offset of the log entry written at that timestamp. New entries to the database would not be written for every log entry, but rather every N entries (say every 3000 entries, corresponding to 1 minute in your case). It’s also important that the file names are not changed by the log rotation process, and have a definite order (for example, the filename is the year-month-day, or an incrementing integer, or a combination of the two). The mapping in the database would be something like:
# timestamp => { filename, byte_offset }
1728663802 => {"2024-10-11.log", 21345}
If you need to retrieve logs in a range of timestamps, you can look up in the database the closets timestamps to the start and end of your range, and read the corresponding file(s) from/until the byte offset. Every day, a scheduled process could remove log files as well as database entries older than the retention.
Since the database would then receive a much lower write load, you can then safely use any solution that suits your needs, and definitely both CubDB and SQLite would work just fine, even with their default options. You also still get the option of downloading the raw log files for offline inspection.
Popular in Questions
Other popular topics
Latest Nerves Threads
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









