High-Performance, Embedded Time-Series Database
Optimized for real-time streaming applications like video, finance, and IoT sensor data
Built for this from day one. Capture dozens of camera streams concurrently while reviewers scrub through history with frame-accurate seeking β no keyframe snapping, no playback lag.
Ultra-fast writes handle multiple camera and sensor streams in real time. Microsecond timestamps and lock-free reads make on-vehicle log replay practical for debugging and ML pipeline development.
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.
Anywhere you have multiple synchronized data streams, monotonic timestamps, and need to seek to an event and replay a window around it, NanoTS is in its element. The same primitives that power surveillance scrubbing are useful across the sciences.
Multi-antenna acquisition with nanosecond timestamp alignment. Capture continuous I/Q samples at line rate and seek directly to events of interest without paying for full-file scans.
Hundreds-to-thousands of electrode channels at kHzβMHz rates. Stream-per-channel storage with fast windowed retrieval around behavioral or stimulus events β the foundational access pattern for spike-sorting and trial analysis.
Continuous 24/7 acquisition from station networks. When an event occurs, pull the same time window from every nearby station instantly β no miniSEED file juggling, no per-query parsing overhead.
Lidar, IMU, GPS, and camera streams captured at native rates with precise time alignment. Frame-accurate scrubbing across heterogeneous sensors makes log replay and dataset curation a first-class workflow.
Memory-mapped storage with lock-free data structures enables maximum throughput with sub-microsecond performance.
Memory-mapped data structures let any number of readers iterate, seek, and scrub concurrently while a writer is appending β no contention, no locks, no surprises.
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 a growable database with 1MB blocks. The file starts at just
// the 64KB header and extends on demand as you write.
nanots_writer::allocate_growable("video.nts", 1024*1024);
nanots_writer db("video.nts");
// 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).
By default, NanoTS files grow on demand β the file starts as just a 64KB header and extends as you write, using a BoltDB-style doubling strategy capped at 1 GiB per grow event. For embedded or fixed-budget deployments you can also allocate a file at a fixed size up front and combine it with auto-recycle mode so the oldest data is reclaimed when the file is full.