Skip to content

Commit 7224436

Browse files
author
Danni Moiseyev
committed
basic functionallity: insert,query, max retention
1 parent a079f03 commit 7224436

File tree

12 files changed

+1502
-0
lines changed

12 files changed

+1502
-0
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "RedisModulesSDK"]
2+
path = RedisModulesSDK
3+
url = ./RedisModulesSDK

LICENSE

Lines changed: 661 additions & 0 deletions
Large diffs are not rendered by default.

RedisModulesSDK

Submodule RedisModulesSDK added at 4c62df8

readme.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Redis TSDB Module
2+
3+
## Overview
4+
With this module you can now store timeseries data effiecently in redis.
5+
The data is stored in a compact way.
6+
7+
## License: AGPL
8+
9+
## Memory model
10+
Each series has a linked list of chunks.
11+
Each chunk has 1+ samples.
12+
Sample is a timestamp+value.
13+
14+
## Features
15+
* Quick inserts (50K samples per sec)
16+
* Query by start time and end-time
17+
* Configurable max retention period
18+
19+
## Build
20+
1. `cd src`
21+
2. `make`
22+
3. `loadmodule redis-tsdb-module.so`
23+
24+
## Cmds
25+
```sql
26+
TS.INSERT key TIMESTAMP value
27+
```
28+
```sql
29+
TS.QUERY key FROM_TIMESTAMP TO_TIMESTAMP
30+
1) (integer) 1486289260
31+
2) (integer) 49994
32+
3) (integer) 1486289261
33+
4) (integer) 49995
34+
5) (integer) 1486289262
35+
6) (integer) 49996
36+
7) (integer) 1486289263
37+
8) (integer) 49997
38+
9) (integer) 1486289264
39+
10) (integer) 49998
40+
11) (integer) 1486289265
41+
12) (integer) 49999
42+
```
43+
```sql
44+
TS.META key
45+
1) lastTimestamp
46+
2) (integer) 1486289265
47+
3) retentionSecs
48+
4) (integer) 0
49+
5) chunkCount
50+
6) (integer) 139
51+
7) maxSamplesPerChunk
52+
8) (integer) 360
53+
```

src/Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#set environment variable RM_INCLUDE_DIR to the location of redismodule.h
2+
ifndef RM_INCLUDE_DIR
3+
RM_INCLUDE_DIR=../RedisModulesSDK
4+
endif
5+
6+
ifndef RMUTIL_LIBDIR
7+
RMUTIL_LIBDIR=../RedisModulesSDK/rmutil
8+
endif
9+
10+
# find the OS
11+
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
12+
13+
# Compile flags for linux / osx
14+
ifeq ($(uname_S),Linux)
15+
SHOBJ_CFLAGS ?= -fno-common -g -ggdb
16+
SHOBJ_LDFLAGS ?= -shared -Bsymbolic
17+
else
18+
SHOBJ_CFLAGS ?= -dynamic -fno-common -g -ggdb
19+
SHOBJ_LDFLAGS ?= -bundle -undefined dynamic_lookup
20+
endif
21+
CFLAGS = -I$(RM_INCLUDE_DIR) -Wall -g -fPIC -lc -lm -std=gnu99 -DREDIS_MODULE_TARGET
22+
CC=gcc
23+
24+
all: rmutil redis-tsdb-module.so
25+
26+
rmutil: FORCE
27+
$(MAKE) -C $(RMUTIL_LIBDIR)
28+
29+
redis-tsdb-module.so: module.o tsdb.o
30+
$(LD) -o $@ module.o tsdb.o $(SHOBJ_LDFLAGS) $(LIBS) -L$(RMUTIL_LIBDIR) -lrmutil -lc
31+
32+
clean:
33+
rm -rf *.xo *.so *.o
34+
35+
FORCE:

src/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# An Example Redis Module
2+
3+
This project is a simple redis module demonstrating basic API usage and `librmutil`.
4+
5+
You can treat it as a basic module template. See the project's [README](../README.md) for more details.

src/module.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include "redismodule.h"
2+
#include "module.h"
3+
#include "rmutil/util.h"
4+
#include "rmutil/strings.h"
5+
#include "rmutil/alloc.h"
6+
// #include "rmutil/test_util.h"
7+
8+
#include "tsdb.h"
9+
10+
#define MYTYPE_ENCODING_VERSION 0
11+
12+
static RedisModuleType *SeriesType;
13+
14+
int TSDB_Meta(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
15+
RedisModule_AutoMemory(ctx);
16+
17+
if (argc != 2) return RedisModule_WrongArity(ctx);
18+
19+
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ|REDISMODULE_WRITE);
20+
Series *series;
21+
22+
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY){
23+
return RedisModule_ReplyWithError(ctx, "TSDB: key does not exist");
24+
} else if (RedisModule_ModuleTypeGetType(key) != SeriesType){
25+
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
26+
} else {
27+
series = RedisModule_ModuleTypeGetValue(key);
28+
}
29+
30+
RedisModule_ReplyWithArray(ctx, 4*2);
31+
32+
RedisModule_ReplyWithSimpleString(ctx, "lastTimestamp");
33+
RedisModule_ReplyWithLongLong(ctx, series->lastTimestamp);
34+
RedisModule_ReplyWithSimpleString(ctx, "retentionSecs");
35+
RedisModule_ReplyWithLongLong(ctx, series->retentionSecs);
36+
RedisModule_ReplyWithSimpleString(ctx, "chunkCount");
37+
RedisModule_ReplyWithLongLong(ctx, series->chunkCount);
38+
RedisModule_ReplyWithSimpleString(ctx, "maxSamplesPerChunk");
39+
RedisModule_ReplyWithLongLong(ctx, series->maxSamplesPerChunk);
40+
41+
return REDISMODULE_OK;
42+
}
43+
44+
int TSDB_Query(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
45+
RedisModule_AutoMemory(ctx);
46+
47+
if (argc != 4) return RedisModule_WrongArity(ctx);
48+
49+
long long start_ts, end_ts;
50+
51+
if (RedisModule_StringToLongLong(argv[2], &start_ts) != REDISMODULE_OK)
52+
RedisModule_ReplyWithError(ctx, "TSDB: start-timestamp is invalid");
53+
if (RedisModule_StringToLongLong(argv[3], &end_ts) != REDISMODULE_OK)
54+
RedisModule_ReplyWithError(ctx, "TSDB: end-timestamp is invalid");
55+
56+
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ|REDISMODULE_WRITE);
57+
Series *series;
58+
59+
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY){
60+
return RedisModule_ReplyWithError(ctx, "TSDB: key does not exist");
61+
} else if (RedisModule_ModuleTypeGetType(key) != SeriesType){
62+
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
63+
} else {
64+
series = RedisModule_ModuleTypeGetValue(key);
65+
}
66+
67+
long long arraylen = 0;
68+
RedisModule_ReplyWithArray(ctx,REDISMODULE_POSTPONED_ARRAY_LEN);
69+
SeriesItertor iterator = SeriesQuery(series, start_ts, end_ts);
70+
Sample *sample;
71+
while ((sample = SeriesItertorGetNext(&iterator)) != NULL ) {
72+
RedisModule_ReplyWithLongLong(ctx, sample->timestamp);
73+
RedisModule_ReplyWithLongLong(ctx, sample->data);
74+
arraylen++;
75+
}
76+
77+
RedisModule_ReplySetArrayLength(ctx,arraylen*2);
78+
return REDISMODULE_OK;
79+
}
80+
81+
int TSDB_Insert(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
82+
RedisModule_AutoMemory(ctx);
83+
84+
if (argc != 4) {
85+
return RedisModule_WrongArity(ctx);
86+
}
87+
88+
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ|REDISMODULE_WRITE);
89+
90+
double timestamp, value;
91+
if ((RedisModule_StringToDouble(argv[3], &value) != REDISMODULE_OK))
92+
return RedisModule_ReplyWithError(ctx,"TSDB: invalid value");
93+
94+
if ((RedisModule_StringToDouble(argv[2], &timestamp) != REDISMODULE_OK))
95+
return RedisModule_ReplyWithError(ctx,"TSDB: invalid timestamp");
96+
97+
Series *series;
98+
99+
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY){
100+
series = TSDB_Create(ctx, argv[1]);
101+
} else if (RedisModule_ModuleTypeGetType(key) != SeriesType){
102+
return RedisModule_ReplyWithError(ctx, "TSDB: the key is not a TSDB key");
103+
} else {
104+
series = RedisModule_ModuleTypeGetValue(key);
105+
}
106+
107+
int retval = SeriesAddSample(series, timestamp, value);
108+
if (retval == TSDB_ERR_TIMESTAMP_TOO_OLD) {
109+
RedisModule_ReplyWithError(ctx, "TSDB: timestamp is too old");
110+
return REDISMODULE_ERR;
111+
} else if (retval != TSDB_OK) {
112+
RedisModule_ReplyWithError(ctx, "TSDB: Unknown Error");
113+
return REDISMODULE_ERR;
114+
} else {
115+
RedisModule_ReplyWithSimpleString(ctx, "OK");
116+
RedisModule_ReplicateVerbatim(ctx);
117+
return REDISMODULE_OK;
118+
}
119+
}
120+
121+
Series* TSDB_Create(RedisModuleCtx *ctx, RedisModuleString *key) {
122+
RedisModuleKey *series = RedisModule_OpenKey(ctx, key, REDISMODULE_READ|REDISMODULE_WRITE);
123+
124+
if (RedisModule_KeyType(series) != REDISMODULE_KEYTYPE_EMPTY) {
125+
return NULL;
126+
}
127+
128+
Series *newSeries = NewSeries();
129+
RedisModule_ModuleTypeSetValue(series, SeriesType, newSeries);
130+
131+
RedisModule_Log(ctx, "info", "created new series");
132+
return newSeries;
133+
}
134+
void series_aof_rewrite(RedisModuleIO *aof, RedisModuleString *key, void *value)
135+
{}
136+
137+
void series_rdb_save(RedisModuleIO *rdb, void *value)
138+
{}
139+
140+
void *series_rdb_load(RedisModuleIO *rdb, int encver)
141+
{
142+
return NULL;
143+
}
144+
145+
int RedisModule_OnLoad(RedisModuleCtx *ctx) {
146+
if (RedisModule_Init(ctx, "tsdb", 1, REDISMODULE_APIVER_1) ==
147+
REDISMODULE_ERR) {
148+
return REDISMODULE_ERR;
149+
}
150+
151+
RedisModuleTypeMethods tm = {
152+
.version = REDISMODULE_TYPE_METHOD_VERSION,
153+
.rdb_load = series_rdb_load,
154+
.rdb_save = series_rdb_save,
155+
.aof_rewrite = series_aof_rewrite,
156+
.mem_usage = SeriesMemUsage,
157+
.free = SeriesFree
158+
};
159+
160+
SeriesType = RedisModule_CreateDataType(ctx, "TSDB-TYPE", 0, &tm);
161+
if (SeriesType == NULL) return REDISMODULE_ERR;
162+
RMUtil_RegisterWriteCmd(ctx, "ts.insert", TSDB_Insert);
163+
RMUtil_RegisterWriteCmd(ctx, "ts.query", TSDB_Query);
164+
RMUtil_RegisterWriteCmd(ctx, "ts.meta", TSDB_Meta);
165+
166+
return REDISMODULE_OK;
167+
}

src/module.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "tsdb.h"
2+
3+
Series * TSDB_Create(RedisModuleCtx *ctx, RedisModuleString *key);

0 commit comments

Comments
 (0)