This document provides comprehensive documentation for the TSDB implementation with DataCrate support.
The TSDB component is designed to efficiently store and query time series data with high throughput and low query latency. It supports structured schema definition, time-based indexing, and SQL-like queries with powerful aggregation capabilities.
Key features:
- DataCrate Organization: Logical grouping of related time series data
- Flexible Schema: Define fields, tags, and retention policies per table
- High Performance: Optimized storage with compression and indexing
- SQL-like Queries: Familiar query syntax with time series extensions
- Aggregation Support: Built-in functions for time series analysis
DataCrates are logical containers similar to databases in traditional RDBMS:
DataCrate "weather_monitoring"
├── Table "sensors" (temperature, humidity, pressure data)
├── Table "alerts" (alert events and notifications)
└── Table "diagnostics" (system health metrics)
- TsdbStorage: Main storage engine managing all datacrates
- DataCrate: Logical container with retention policy and metadata
- Table: Collection of time series records with defined schema
- TableStorage: Physical storage for table data with indexing
- TimeSeriesRecord: Individual timestamped data point
use my_database_app::tsdb::storage::TsdbStorage;
use my_database_app::compression::CompressionType;
// Create TSDB storage with Zstd compression
let tsdb = TsdbStorage::new("./data", CompressionType::Zstd)?;use my_database_app::tsdb::types::{CrateCommand, RetentionPolicy};
use chrono::Duration;
let cmd = CrateCommand::CreateCrate {
name: "weather_data".to_string(),
description: Some("Weather station monitoring".to_string()),
retention_policy: RetentionPolicy::Duration(Duration::days(30)),
};
let result = tsdb.execute_crate_command(cmd)?;use my_database_app::tsdb::types::{TableCommand, TableSchema, FieldType};
use std::collections::BTreeMap;
// Define schema
let mut fields = BTreeMap::new();
fields.insert("temperature".to_string(), FieldType::Float);
fields.insert("humidity".to_string(), FieldType::Float);
fields.insert("status".to_string(), FieldType::String);
let schema = TableSchema {
fields,
tags: vec!["location".to_string(), "sensor_id".to_string()],
retention_policy: Some(RetentionPolicy::Duration(Duration::days(7))),
};
let cmd = TableCommand::CreateTable {
crate_name: "weather_data".to_string(),
table_name: "sensors".to_string(),
schema,
};
tsdb.execute_table_command(cmd)?;use my_database_app::tsdb::types::{DataCommand, TimeSeriesRecord, FieldValue};
use chrono::Utc;
// Prepare data
let mut fields = BTreeMap::new();
fields.insert("temperature".to_string(), FieldValue::Float(23.5));
fields.insert("humidity".to_string(), FieldValue::Float(45.0));
fields.insert("status".to_string(), FieldValue::String("normal".to_string()));
let mut tags = BTreeMap::new();
tags.insert("location".to_string(), "office_a".to_string());
tags.insert("sensor_id".to_string(), "temp_001".to_string());
let record = TimeSeriesRecord {
timestamp: Utc::now(),
fields,
tags,
};
// Insert data
let cmd = DataCommand::Insert {
crate_name: "weather_data".to_string(),
table_name: "sensors".to_string(),
record,
};
tsdb.execute_data_command(cmd)?;use my_database_app::tsdb::query::parse_query;
// Parse and execute query
let query = parse_query("SELECT temperature, humidity FROM sensors WHERE location = 'office_a' LIMIT 10")?;
let results = tsdb.execute_query(&query)?;
// Process results
for row in results {
for (field, value) in row {
println!("{}: {}", field, value);
}
}The TSDB supports both structured commands and legacy simplified commands.
TSDB_CREATE_CRATE <name> <description> <retention_policy_json>
Example:
TSDB_CREATE_CRATE weather_monitoring "Weather station data" {"Duration":2592000}
TSDB_LIST_CRATES
TSDB_GET_CRATE_INFO <name>
TSDB_DELETE_CRATE <name>
TSDB_CREATE_TABLE <crate_name> <table_name> <schema_json>
Schema format:
{
"fields": {
"temperature": "Float",
"humidity": "Float",
"status": "String",
"active": "Boolean",
"count": "Integer"
},
"tags": ["location", "sensor_id", "building"],
"retention_policy": {"Duration": 604800} // Optional, 7 days in seconds
}Example:
TSDB_CREATE_TABLE weather_monitoring sensors {"fields":{"temperature":"Float","humidity":"Float"},"tags":["location","sensor_id"],"retention_policy":{"Duration":604800}}
TSDB_LIST_TABLES <crate_name>
TSDB_GET_TABLE_SCHEMA <crate_name> <table_name>
TSDB_DELETE_TABLE <crate_name> <table_name>
TSDB_INSERT <crate_name> <table_name> <record_json>
Record format:
{
"timestamp": "2025-06-06T12:00:00Z",
"fields": {
"temperature": 72.5,
"humidity": 45.0,
"status": "normal"
},
"tags": {
"location": "office_a",
"sensor_id": "temp_001"
}
}Example:
TSDB_INSERT weather_monitoring sensors {"timestamp":"2025-06-06T12:00:00Z","fields":{"temperature":72.5,"humidity":45.0},"tags":{"location":"office_a","sensor_id":"temp_001"}}
TSDB_QUERY "<sql_query>"
TSDB_DELETE <crate_name> <table_name> <time_range_json> <tags_json>
For backward compatibility and simplified usage:
TS.CREATE "<schema_json>"
Schema format:
{
"name": "measurement_name",
"fields": {
"field1": 0.0, // Float field (initialize with 0.0)
"field2": 0, // Integer field (initialize with 0)
"field3": "", // String field (initialize with "")
"field4": false // Boolean field (initialize with false)
},
"retention_policy": {
"Duration": 2592000 // Retention in seconds (30 days)
}
}Example:
TS.CREATE "{\"name\":\"weather\",\"fields\":{\"temperature\":0.0,\"humidity\":0.0,\"pressure\":0.0},\"retention_policy\":{\"Duration\":2592000}}"
TS.INSERT <measurement_name> "<record_json>"
Example:
TS.INSERT weather "{\"timestamp\":\"2025-06-06T12:00:00Z\",\"fields\":{\"temperature\":72.5,\"humidity\":45.0,\"pressure\":1013.2},\"tags\":{\"location\":\"NYC\",\"station\":\"central\"}}"
TS.QUERY "<sql_query>"
TS.LIST
The TSDB supports a comprehensive SQL-like query language designed for time series data.
SELECT <fields>
FROM <table_name>
[WHERE <conditions>]
[GROUP BY <grouping>]
[LIMIT <number>]-- Select specific fields
SELECT temperature, humidity FROM sensors;
-- Select all fields
SELECT * FROM sensors;
-- Select with aggregation
SELECT AVG(temperature), MAX(humidity), COUNT(*) FROM sensors;-- Time range queries
SELECT * FROM sensors
WHERE TIME >= '2025-06-01T00:00:00Z'
AND TIME <= '2025-06-02T00:00:00Z';
-- Relative time
SELECT * FROM sensors
WHERE TIME >= '2025-06-06T00:00:00Z';-- Single tag filter
SELECT temperature FROM sensors
WHERE location = 'office_a';
-- Multiple tag filters
SELECT * FROM sensors
WHERE location = 'office_a'
AND sensor_id = 'temp_001';-- Numeric comparisons
SELECT * FROM sensors
WHERE temperature > 25.0
AND humidity < 50.0;
-- String comparisons
SELECT * FROM sensors
WHERE status = 'normal';COUNT()- Count of recordsSUM(field)- Sum of field valuesAVG(field)- Average of field valuesMIN(field)- Minimum field valueMAX(field)- Maximum field valueFIRST(field)- First value chronologicallyLAST(field)- Last value chronologicallyPERCENTILE_XX(field)- Percentile functions (e.g., PERCENTILE_95)
-- Basic aggregation
SELECT COUNT(*), AVG(temperature), MAX(humidity) FROM sensors;
-- Aggregation with filtering
SELECT AVG(temperature) FROM sensors
WHERE location = 'office_a'
AND TIME >= '2025-06-06T00:00:00Z';
-- Multiple aggregations
SELECT
location,
COUNT(*) as reading_count,
AVG(temperature) as avg_temp,
MIN(temperature) as min_temp,
MAX(temperature) as max_temp,
PERCENTILE_95(temperature) as p95_temp
FROM sensors
GROUP BY location;Group results by time intervals using GROUP BY TIME(interval):
- Seconds:
1s,30s,45s - Minutes:
1m,5m,15m,30m - Hours:
1h,3h,6h,12h - Days:
1d,7d - Weeks:
1w,2w - Months:
1M,3M,6M - Years:
1y
-- Hourly averages
SELECT AVG(temperature), MAX(humidity)
FROM sensors
GROUP BY TIME(1h);
-- Daily summaries
SELECT
COUNT(*) as readings_per_day,
AVG(temperature) as daily_avg_temp,
MIN(temperature) as daily_min_temp,
MAX(temperature) as daily_max_temp
FROM sensors
GROUP BY TIME(1d);
-- 15-minute intervals with filtering
SELECT location, AVG(temperature)
FROM sensors
WHERE TIME >= '2025-06-06T00:00:00Z'
GROUP BY location, TIME(15m);-- Limit number of results
SELECT * FROM sensors LIMIT 100;
-- Limit with ordering (implicit time ordering)
SELECT temperature, humidity FROM sensors
WHERE location = 'office_a'
LIMIT 50;SELECT sensor_id, temperature, humidity
FROM sensors
WHERE TIME >= '2025-06-06T00:00:00Z'
AND TIME <= '2025-06-06T23:59:59Z'
AND location = 'office_a'
AND temperature BETWEEN 20.0 AND 30.0
AND status = 'normal'
LIMIT 1000;-- Hourly temperature trends for specific location
SELECT
AVG(temperature) as avg_temp,
MIN(temperature) as min_temp,
MAX(temperature) as max_temp,
COUNT(*) as reading_count
FROM sensors
WHERE location = 'office_a'
AND TIME >= '2025-06-01T00:00:00Z'
GROUP BY TIME(1h);-- Compare average temperatures across locations
SELECT
location,
AVG(temperature) as avg_temp,
PERCENTILE_95(temperature) as p95_temp,
COUNT(*) as reading_count
FROM sensors
WHERE TIME >= '2025-06-01T00:00:00Z'
GROUP BY location;- 64-bit floating point numbers
- Used for decimal measurements (temperature, pressure, etc.)
- Example:
23.5,1013.25
- 64-bit signed integers
- Used for counts, IDs, whole number measurements
- Example:
42,-10,1000
- UTF-8 encoded text
- Used for categorical data, status values, descriptions
- Example:
"normal","sensor_001","critical"
- True/false values
- Used for binary states, flags, conditions
- Example:
true,false
- Contain the actual measured values
- Are indexed for range queries
- Support aggregation functions
- Examples: temperature, pressure, count, voltage
- Contain metadata about the measurement
- Are indexed for exact-match filtering
- Used for grouping and filtering
- Should have low cardinality (limited unique values)
- Examples: location, sensor_id, device_type, status
{"Duration": 2592000} // 30 days in secondsCommon durations:
- 1 hour:
3600 - 1 day:
86400 - 1 week:
604800 - 30 days:
2592000 - 1 year:
31536000
{"Forever": null}Data is kept indefinitely (use carefully for storage management).
When you need to modify schemas:
- Adding fields: Create new table with additional fields
- Removing fields: Fields not in new records are simply omitted
- Changing types: Requires data migration to new table
- Adding tags: New tags can be added to new records
- Removing tags: Tags not in new records are omitted
Schema format:
{
"name": "measurement_name",
"fields": {
"field1": 0.0, // Float field (initialize with 0.0)
"field2": 0, // Integer field (initialize with 0)
"field3": "", // String field (initialize with "")
"field4": false // Boolean field (initialize with false)
},
"retention_policy": {
"Duration": 2592000 // Retention in seconds (30 days)
}
}TS.INSERT <measurement> "<record_json>"
Example:
TS.INSERT weather "{"timestamp":"2025-06-06T12:00:00Z","fields":{"temperature":72.5,"humidity":45.0,"pressure":1013.2},"tags":{"location":"NYC","station":"central"}}"
Record format:
{
"timestamp": "2025-06-06T12:00:00Z", // ISO 8601 timestamp
"fields": {
"field1": 72.5, // Value must match field type in schema
"field2": 1000
},
"tags": { // Tags are optional metadata for filtering
"location": "NYC",
"host": "server1"
}
}TS.QUERY "<sql_query>"
The SQL query supports SELECT, FROM, WHERE, GROUP BY, and LIMIT clauses.
Examples:
TS.QUERY "SELECT temperature, humidity FROM weather WHERE TIME >= '2025-06-01T00:00:00Z' AND TIME <= '2025-06-07T00:00:00Z'"
TS.QUERY "SELECT AVG(temperature), MAX(humidity) FROM weather WHERE location = 'NYC' GROUP BY TIME(1h) LIMIT 24"
Query format:
SELECT <fields> FROM <measurement> [WHERE <conditions>] [GROUP BY <group>] [LIMIT <n>]
- SELECT: comma-separated field names or aggregate functions
- Supported aggregates: COUNT(), SUM(), AVG(), MIN(), MAX(), FIRST(), LAST(), PERCENTILE_XX()
- WHERE: filter by time range and/or tags
- Time format: TIME >= '2025-06-01T00:00:00Z' AND TIME <= '2025-06-07T00:00:00Z'
- Tag format: tag_name = 'tag_value'
- GROUP BY: group results, e.g., GROUP BY TIME(1h) for hourly grouping
- Supported intervals: TIME(Xs), TIME(Xm), TIME(Xh), TIME(Xd), TIME(Xw), TIME(XM), TIME(Xy)
- LIMIT: maximum number of results to return
TS.LIST
Lists all available measurements in the TSDB.
The TSDB uses a structured data model with:
- Measurements: Similar to tables in a relational database
- Fields: The actual data values (numeric, string, boolean)
- Tags: Metadata for filtering and categorizing data
- Timestamp: Required for each record, in UTC
- Group similar data in the same measurement
- Use tags for frequently queried metadata
- Limit the number of fields per measurement
- Choose appropriate retention policies for your data
- Use aggregation for long time ranges to reduce query time
- Retention Policies: Automatically expire old data
- Aggregation Functions: Powerful data analysis capabilities
- Time Bucketing: Group results by time intervals
- High Write Throughput: Optimized for sensor and monitoring data
- Efficient Compression: Reduces storage requirements
- IoT Sensor Data: Store and analyze sensor readings
- Application Metrics: Monitor application performance
- Financial Data: Track market prices and trading volumes
- Weather Data: Record and analyze weather conditions
- User Analytics: Track user behavior over time