High-Performance, Embedded Time-Series Database
Optimized for real-time streaming applications like video, finance, and IoT sensor data
Perfect for embedded robotics systems with continuous camera feeds. Pre-allocated storage eliminates administration - just set your buffer size and always have the newest frames for real-time processing.
Ultra-fast writes handle multiple camera streams in real-time. Fixed storage allocation ensures consistent performance without filesystem administration in safety-critical autonomous systems.
High-frequency sensor readings, efficient storage of numeric telemetry, and time-based queries and analysis.
Trade tick data with microsecond precision, market data replay systems, and low-latency historical queries.
Memory-mapped storage with lock-free data structures enables maximum throughput with sub-microsecond performance.
Avoid filesystem fragmentation and administration hassles with auto-recycle mode for continuous operation.
Trade-off between performance and data safety with configurable block sizes to match your application needs.
Automatic detection and recovery from unexpected shutdowns with frame-level atomicity guarantees.
Store different data streams in the same database file with independent write contexts and iterators.
Free for commercial and non-commercial use under the Apache 2.0 license.
git clone https://github.com/dicroce/nanots.git
Copy the 4 source files from the nanots/amalgamated_src/ dir into your projects source directories and add them to your build.
Or use CMake to build as a static library and link it (mkdir build && cd build && cmake .. && make)
#include "nanots.h"
// Create database with 1MB blocks, 16 total blocks
nanots_writer::allocate("video.nts", 1024*1024, 16);
// Open the db in auto recycle mode
nanots_writer db("video.nts", true);
// Create write context for a stream
auto wctx = db.create_write_context("camera_1", "stream metadata");
// Write frames
uint8_t frame_data[] = {/* video frame bytes */};
db.write(wctx, frame_data, sizeof(frame_data), timestamp_us, flags);
#include "nanots.h"
// Create iterator for a stream
nanots_iterator iter("video.nts", "camera_1");
// Iterate through all frames
while (iter.valid()) {
auto& frame = *iter;
process_frame(frame.data, frame.size, frame.timestamp, frame.flags);
++iter;
}
// Or seek to specific timestamp
if (iter.find(target_timestamp)) {
// Found first frame >= target_timestamp
auto& frame = *iter;
// ... process frame
}
βββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ¬ββββββββββββββ βFile Header β Block 1 β Block 2 β Block N β β (64KB) β (1MB+) β (1MB+) β (1MB+) β βββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ΄ββββββββββββββ / \ / \ βββββββββββββββ¬ββββββββββββββββββ¬βββββββββββββββββββββββββββ βBlock Header β Block Index β Block Data β β (16 bytes) β (variable) -> β <- (variable) β βββββββββββββββ΄ββββββββββββββββββ΄βββββββββββββββββββββββββββ
Hybrid approach combining SQLite for metadata with memory-mapped binary files for data. Block size is configurable and tunable for different applications.
Durability is configurable through block sizes. Writers sync to disk during block transitions. Smaller blocks provide better durability but may slow writes due to contention. We recommend starting with large blocks (50MB) and adjusting as needed.
NanoTS allows any number of writing threads as long as each writes to its own logical stream, plus any number of read threads (1 writer per stream + N readers).
NanoTS pre-allocates storage space, which prevents server downtime from disk space issues, enables recycling of oldest data when full, and eliminates filesystem fragmentation concerns.