The public API is declared in include/tsedge.h. Internal structures are not
exposed; tsedge_db is opaque.
All functions return TSEDGE_OK on success or a negative TSEDGE_ERR_* status
on failure.
Opens or creates a database directory.
Parameters:
path: filesystem path to the database directory.out_db: receives the opened database handle.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_INVALID_ARGUMENT,TSEDGE_ERR_IO,TSEDGE_ERR_NO_MEMORY, orTSEDGE_ERR_CORRUPTon failure.
Example:
tsedge_db* db = NULL;
int rc = tsedge_open("./demo_db", &db);Flushes pending data and closes a database handle.
Parameters:
db: database handle, orNULL.
Returns:
TSEDGE_OKon success.- An error status if flushing pending data fails.
Example:
tsedge_close(db);Creates a named time series in the database. If the series already exists, the operation succeeds without creating a duplicate.
Parameters:
db: database handle.name: series name, such asmotor.temperature.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_INVALID_ARGUMENT,TSEDGE_ERR_IO, orTSEDGE_ERR_NO_MEMORYon failure.
Example:
tsedge_create_series(db, "motor.temperature");Sets how aggressively TSEdge flushes WAL entries.
TSEDGE_DURABILITY_FAST: highest throughput. WAL entries may stay in memory until the WAL buffer fills, flush runs, or the database closes. A process crash may lose the latest buffered WAL entries.TSEDGE_DURABILITY_BALANCED: buffered WAL with a smaller flush threshold. This is a compromise between write throughput and recovery freshness.TSEDGE_DURABILITY_STRICT: default mode. WAL is flushed on every append or batch, so crash recovery sees accepted points that reached the append call.
tsedge_flush, tsedge_flush_all and tsedge_close flush the WAL buffer.
Example:
tsedge_set_durability(db, TSEDGE_DURABILITY_FAST);TSEdge can enforce a soft disk quota for database files:
tsedge_set_disk_quota(db, 128ull * 1024ull * 1024ull);
tsedge_enforce_disk_quota(db);The quota is a runtime setting on the opened tsedge_db; it is not stored in
manifest.txt. Passing 0 to tsedge_set_disk_quota disables the limit.
When the database is larger than the configured limit, TSEdge deletes the
oldest sealed segment_*.tse files first. Active segment files, the last
segment file of every series, manifest.txt, wal.log, metadata.txt, and
non-segment files are not removed by quota cleanup.
The limit is soft: the database may remain larger than the quota when only
active or last segment files are left. In that case
tsedge_enforce_disk_quota returns TSEDGE_ERR_QUOTA_EXCEEDED.
uint64_t quota = 0;
tsedge_get_disk_quota(db, "a);Quota cleanup is also attempted after flush operations and database close.
Appends one point to an existing series. A point contains an int64_t
timestamp and a double value.
Parameters:
db: database handle.series_name: existing series name.timestamp: point timestamp.value: point value.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, I/O failures, or allocation failures.
Example:
tsedge_append(db, "motor.temperature", 1710000000000LL, 72.4);For frequent writes to the same series, resolve the series once and append through a handle. This avoids repeated lookup by string name.
The handle is owned by the database. Do not free it. It is valid until the series is deleted or the database is closed.
tsedge_series_handle* temperature = NULL;
tsedge_get_series_handle(db, "motor.temperature", &temperature);
for (int64_t i = 0; i < 1000000; ++i) {
tsedge_append_handle(db, temperature, i, 20.0);
}Use tsedge_append_batch_handle when points are already available as an array.
The string-based tsedge_append and tsedge_append_batch remain available and
are simpler for occasional writes.
Appends multiple points to an existing series. The public argument validation
and series lookup are performed once for the whole batch, while each point still
uses the same WAL-before-buffer append path as tsedge_append.
Parameters:
db: database handle.series_name: existing series name.points: array of points to append.count: number of points in the array.
Returns:
TSEDGE_OKon success.TSEDGE_OKwhencountis0; in this casepointsmay beNULL.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, I/O failures, or allocation failures.
If an error happens after some points have already been written, those earlier points may remain accepted and visible after recovery. The function does not provide all-or-nothing batch transactions.
Example:
tsedge_point points[3] = {
{1710000000000LL, 72.4},
{1710000001000LL, 72.5},
{1710000002000LL, 72.6},
};
tsedge_append_batch(db, "motor.temperature", points, 3);Flushes buffered points of one series into a segment file. If the buffer is empty, the call succeeds and does nothing.
The function uses the same internal block/segment write path as automatic flushes. After success, the block index and WAL are updated.
Parameters:
db: database handle.series_name: existing series name.
Returns:
TSEDGE_OKon success, including an empty buffer.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, rotation failures, I/O failures or allocation failures.
Example:
tsedge_append(db, "motor.temperature", timestamp, value);
tsedge_flush(db, "motor.temperature");
tsedge_export_csv(db, "motor.temperature", from, to, "temperature.csv");Flushes buffered points of all series in the database. Empty buffers are ignored. If one series fails to flush, the function returns that error.
After a successful call, WAL contains only data that is still not present in segment files.
Parameters:
db: database handle.
Returns:
TSEDGE_OKon success.- Other error statuses for invalid arguments, rotation failures, I/O failures or allocation failures.
Example:
tsedge_flush_all(db);Checks the structure of a database directory without modifying files. The
function verifies the root directory, manifest.txt, series metadata,
segment_*.tse files, block headers, payload boundaries and WAL entries.
The output structure is:
typedef struct {
size_t series_count;
size_t segment_count;
size_t block_count;
size_t wal_entry_count;
size_t error_count;
char first_error_path[256];
char first_error_message[256];
} tsedge_verify_report;Parameters:
db_path: filesystem path to an existing TSEdge database directory.report: receives counters and the first error, if any.
Returns:
TSEDGE_OKwhen the database is structurally valid.TSEDGE_ERR_CORRUPTwhen a corrupt file or invalid layout is found.- Other error statuses for invalid arguments, I/O failures or allocation failures.
Example:
tsedge_verify_report report;
int rc = tsedge_verify("demo_db", &report);
if (rc == TSEDGE_OK) {
printf("database is valid\n");
} else {
printf("database is corrupted: %s\n", report.first_error_message);
}Returns a copied list of all series loaded in an open database. The function uses the database's in-memory series registry and lightweight stats.
The output structure is:
typedef struct {
char name[TSEDGE_MAX_SERIES_NAME + 1u];
uint64_t total_points;
uint32_t segment_count;
uint32_t block_count;
uint64_t compressed_size_bytes;
} tsedge_series_info;Parameters:
db: database handle.out_series: receives a newly allocated array, orNULLfor an empty database.out_count: receives the number of entries.
Returns:
TSEDGE_OKon success.TSEDGE_OKwith*out_series == NULLand*out_count == 0for an empty database.TSEDGE_ERR_INVALID_ARGUMENTfor invalid pointers.TSEDGE_ERR_NO_MEMORYif the list cannot be allocated.
The caller must release the array with tsedge_free_series_list.
Example:
tsedge_series_info* series = NULL;
size_t count = 0;
int rc = tsedge_list_series(db, &series, &count);
if (rc == TSEDGE_OK) {
for (size_t i = 0; i < count; ++i) {
printf("%s\n", series[i].name);
}
tsedge_free_series_list(series);
}Deletes one time series from an open database. The function removes the series
from the in-memory registry, frees its buffer and block index, and deletes the
series directory with metadata.txt and segment_*.tse files.
Before removing files, TSEdge flushes buffers through the normal WAL truncation path. This prevents pending WAL entries from recreating the deleted series after the database is reopened.
Parameters:
db: database handle.series_name: name of the series to delete.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_INVALID_ARGUMENTfor invalid arguments.TSEDGE_ERR_NOT_FOUNDif the series does not exist.TSEDGE_ERR_IOif files cannot be removed or metadata cannot be updated.
Example:
int rc = tsedge_delete_series(db, "air.temperature");
if (rc != TSEDGE_OK) {
fprintf(stderr, "failed to delete series\n");
}Returns lightweight statistics for an existing series. The function uses the in-memory block index, the current unflushed buffer and segment file sizes. It does not decompress block payloads or scan all stored points.
The output structure is:
typedef struct {
size_t block_count;
size_t buffered_points;
size_t total_indexed_points;
size_t segment_count;
int has_time_range;
int64_t min_timestamp;
int64_t max_timestamp;
uint32_t active_segment_id;
uint64_t segment_size_bytes;
uint64_t total_segment_size_bytes;
uint64_t raw_size_estimate_bytes;
uint64_t compressed_size_bytes;
double compression_ratio;
double bytes_per_point;
} tsedge_series_stats;Fields:
block_count: number of blocks currently present in the in-memory block index.buffered_points: number of points still kept in the memory buffer.total_indexed_points: sum ofpoint_countacross indexed blocks.segment_count: number of existingsegment_*.tsefiles for the series.has_time_range:1when the series has at least one point in blocks or buffer, otherwise0.min_timestampandmax_timestamp: timestamp range across indexed blocks and buffered points whenhas_time_rangeis1.active_segment_id: id used for the next block flush.segment_size_bytes: total size of all segment files. This keeps older callers useful after segment rotation.total_segment_size_bytes: explicit total size of all segment files.raw_size_estimate_bytes: estimated uncompressed size aspoint_count * sizeof(tsedge_point).compressed_size_bytes: actual bytes occupied by the series segment files.compression_ratio:raw_size_estimate_bytes / compressed_size_bytes, or0.0when there are no segment bytes yet.bytes_per_point: average segment bytes per point, or0.0for an empty series.
Parameters:
db: database handle.series_name: existing series name.out_stats: receives the statistics.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments.
Example:
tsedge_series_stats stats;
int rc = tsedge_get_series_stats(db, "motor.temperature", &stats);
if (rc == TSEDGE_OK && stats.has_time_range) {
printf("segments=%zu active=%u blocks=%zu buffered=%zu range=%lld..%lld\n",
stats.segment_count,
stats.active_segment_id,
stats.block_count,
stats.buffered_points,
(long long)stats.min_timestamp,
(long long)stats.max_timestamp);
}Deletes old data from a series at segment-file granularity. A segment is removed
only when every block in that file is older than older_than_timestamp, meaning
the segment maximum timestamp is less than the threshold. Segments that
partially overlap the threshold are kept unchanged.
Before deletion, TSEdge flushes the current in-memory buffer. This keeps the WAL consistent with the segment files and makes the retention decision based on durable block metadata. After deleting files, the in-memory block index is rebuilt from the remaining segment files.
Parameters:
db: database handle.series_name: existing series name.older_than_timestamp: delete segments whose maximum timestamp is strictly less than this value.
Returns:
TSEDGE_OKon success, including when no segment matches the threshold.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, flush failures, I/O failures or corrupt segment data.
Example:
int rc = tsedge_delete_before(db, "motor.temperature", 1710001000000LL);
if (rc != TSEDGE_OK) {
fprintf(stderr, "retention failed: %s\n", tsedge_strerror(rc));
}This function does not rewrite partially old segments. Exact deletion inside a segment requires compaction, which is not implemented in the current prototype.
Reads points from a series over an inclusive timestamp range and passes each matching point to a callback.
Parameters:
db: database handle.series_name: existing series name.from_timestamp: inclusive range start.to_timestamp: inclusive range end.callback: function called for each matching point.user_data: pointer passed through to the callback.
If the callback returns a non-zero value, the range scan stops early and
tsedge_read_range still returns TSEDGE_OK. This is intended for callers that
only need the first matching point or want to cancel a scan.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, I/O failures, or corrupt data.
Example:
static int print_point(const tsedge_point* point, void* user_data) {
(void)user_data;
printf("%lld %.17g\n", (long long)point->timestamp, point->value);
return 0;
}
tsedge_read_range(db, "motor.temperature", 1710000000000LL,
1710000100000LL, print_point, NULL);Computes a streaming aggregate over an inclusive timestamp range.
Parameters:
db: database handle.series_name: existing series name.from_timestamp: inclusive range start.to_timestamp: inclusive range end.type: one ofTSEDGE_AGG_MIN,TSEDGE_AGG_MAX,TSEDGE_AGG_SUM,TSEDGE_AGG_AVG, orTSEDGE_AGG_COUNT.out_result: receives the aggregate value.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_NOT_FOUNDif the series or requested non-count aggregate data is not found.- Other error statuses for invalid arguments, I/O failures, or corrupt data.
Example:
double avg = 0.0;
tsedge_aggregate(db, "motor.temperature", 1710000000000LL,
1710000100000LL, TSEDGE_AGG_AVG, &avg);Computes count, min, max and avg for non-empty time windows. This is
intended for graph downsampling and overview queries where the caller needs a
compact representation instead of every raw point.
Window bounds are half-open: [window_start, window_end). A point exactly at
window_end belongs to the next window. Empty windows are omitted from the
returned array.
Parameters:
db: database handle.series_name: existing series name.start_time: inclusive query start.end_time: exclusive query end.window_size: positive window width in timestamp units.out_windows: receives an allocated array of non-empty windows.out_count: receives the number of returned windows.
Returns:
TSEDGE_OKon success. If no data matches,*out_windows == NULLand*out_count == 0.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, I/O failures, memory allocation failures, or corrupt data.
The returned array must be released with tsedge_free_window_aggregates.
Example:
tsedge_window_aggregate* windows = NULL;
size_t count = 0;
int rc = tsedge_aggregate_windowed(
db,
"motor.temperature",
1710000000000LL,
1710003600000LL,
60000,
&windows,
&count
);
if (rc == TSEDGE_OK) {
for (size_t i = 0; i < count; ++i) {
printf("%lld,%lld,%llu,%.3f\n",
(long long)windows[i].window_start,
(long long)windows[i].window_end,
(unsigned long long)windows[i].count,
windows[i].avg);
}
tsedge_free_window_aggregates(windows);
}Exports points from an inclusive timestamp range to a CSV file.
Parameters:
db: database handle.series_name: existing series name.from_timestamp: inclusive range start.to_timestamp: inclusive range end.output_path: path to the output CSV file.
Returns:
TSEDGE_OKon success.TSEDGE_ERR_NOT_FOUNDif the series does not exist.- Other error statuses for invalid arguments, I/O failures, or corrupt data.
CSV format:
timestamp,value
1710000000000,72.4Example:
tsedge_export_csv(db, "motor.temperature", 1710000000000LL,
1710000100000LL, "temperature.csv");Returns a static human-readable string for a TSEdge status code.
Parameters:
status: status code returned by a TSEdge API function.
Returns:
- A static string. The caller must not free it.
Example:
int rc = tsedge_create_series(db, "motor.temperature");
if (rc != TSEDGE_OK) {
fprintf(stderr, "%s\n", tsedge_strerror(rc));
}