NanoTS

High-Performance, Embedded Time-Series Database
Optimized for real-time streaming applications like video, finance, and IoT sensor data

Blazing Fast Performance

8.83ΞΌs
Write time on SSD
113K+
Writes/second sustained
<1ΞΌs
Read time on SSD
Lock-free
Storage data structure

Use Cases

πŸ€–

Robotic Vision

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.

πŸš—

Self-Driving Cars

Ultra-fast writes handle multiple camera streams in real-time. Fixed storage allocation ensures consistent performance without filesystem administration in safety-critical autonomous systems.

πŸ“Š

IoT Sensor Data

High-frequency sensor readings, efficient storage of numeric telemetry, and time-based queries and analysis.

πŸ’°

Financial Data

Trade tick data with microsecond precision, market data replay systems, and low-latency historical queries.

Key Features

⚑

Ultra-Fast Writes

Memory-mapped storage with lock-free data structures enables maximum throughput with sub-microsecond performance.

πŸ’Ύ

Preallocated Storage

Avoid filesystem fragmentation and administration hassles with auto-recycle mode for continuous operation.

πŸ”§

Configurable Durability

Trade-off between performance and data safety with configurable block sizes to match your application needs.

πŸ”„

Crash Recovery

Automatic detection and recovery from unexpected shutdowns with frame-level atomicity guarantees.

πŸ”€

Multiple Streams

Store different data streams in the same database file with independent write contexts and iterators.

πŸ”“

Open Source

Free for commercial and non-commercial use under the Apache 2.0 license.

Quick Start

Building

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)
                    
Writing Data
#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);
Reading Data
#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
}

Architecture

Storage Layout

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚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.

Frequently Asked Questions

How durable is NanoTS?

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.

How many writers and readers are supported?

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).

What about pre-allocated storage?

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.