Skip to content

Commit 2311a5e

Browse files
committed
feat: allow specify only timestamp precision
1 parent 64e5e3e commit 2311a5e

File tree

10 files changed

+131
-19
lines changed

10 files changed

+131
-19
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22
## unreleased
3+
### Features
4+
- [201](https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino/pull/201) - Added option to specify timestamp precision and do not send timestamp. Set using `WriteOption::useServerTimestamptrue)`.
5+
36
### Fixes
47
- [200](https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino/pull/200) - Backward compatible compilation. Solves _marked 'override', but does not override_ errors.
58

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ The client has to be configured with a time precision. The default settings is t
181181
client.setWriteOptions(WriteOptions().writePrecision(WritePrecision::MS));
182182
```
183183
When a write precision is configured, the client will automatically assign the current time to the timestamp of each written point which doesn't have a timestamp assigned.
184+
Automated assigning of timestamp can be turned off by using `WriteOption::useServerTimestamp(true)`. Client will still specify a timestamp precision for a server.
184185

185186
If you want to manage timestamp on your own, there are several ways to set the timestamp explicitly.
186187
- `setTime(WritePrecision writePrecision)` - Sets the timestamp to the actual time in the desired precision. The same precision must set in WriteOptions.

src/InfluxDbClient.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ bool InfluxDBClient::setWriteOptions(const WriteOptions & writeOptions) {
226226
_writeOptions._maxRetryInterval = writeOptions._maxRetryInterval;
227227
_writeOptions._maxRetryAttempts = writeOptions._maxRetryAttempts;
228228
_writeOptions._defaultTags = writeOptions._defaultTags;
229+
_writeOptions._useServerTimestamp = writeOptions._useServerTimestamp;
229230
return true;
230231
}
231232

@@ -531,7 +532,7 @@ void InfluxDBClient::dropCurrentBatch() {
531532
}
532533

533534
String InfluxDBClient::pointToLineProtocol(const Point& point) {
534-
return point.createLineProtocol(_writeOptions._defaultTags);
535+
return point.createLineProtocol(_writeOptions._defaultTags, _writeOptions._useServerTimestamp);
535536
}
536537

537538
bool InfluxDBClient::validateConnection() {

src/InfluxDbClient.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class InfluxDBClient {
9999
// Returns true if setting was successful. Otherwise check getLastErrorMessage() for an error.
100100
// Example:
101101
// client.setHTTPOptions(HTTPOptions().httpReadTimeout(20000)).
102-
bool setHTTPOptions(const HTTPOptions &httpOptions);
102+
bool setHTTPOptions(const HTTPOptions &httpOptions);
103103
// Sets connection parameters for InfluxDB 2
104104
// Must be called before calling any method initiating a connection to server.
105105
// serverUrl - url of the InfluxDB 2 server (e.g. https//localhost:8086)

src/Options.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ class WriteOptions {
6262
// Maximum count of retry attempts of failed writes, default 3
6363
uint16_t _maxRetryAttempts;
6464
// Default tags. Default tags are added to every written point.
65-
// There cannot be duplicate tags in default tags and tags included in a point.
65+
// There cannot be the same tags in the default tags and among the tags included with a point.
6666
String _defaultTags;
67+
// Let server assign timestamp in given precision. Do not sent timestamp.
68+
bool _useServerTimestamp;
6769
public:
6870
WriteOptions():
6971
_writePrecision(WritePrecision::NoTime),
@@ -72,17 +74,33 @@ class WriteOptions {
7274
_flushInterval(60),
7375
_retryInterval(5),
7476
_maxRetryInterval(300),
75-
_maxRetryAttempts(3) {
77+
_maxRetryAttempts(3),
78+
_useServerTimestamp(false) {
7679
}
80+
// Sets timestamp precision. If timestamp precision is set, but a point does not have a timestamp, timestamp is automatically assigned from the device clock.
81+
// If useServerTimestamp is set to true, timestamp is not sent, only precision is specified for the server.
7782
WriteOptions& writePrecision(WritePrecision precision) { _writePrecision = precision; return *this; }
83+
// Sets number of points that will be written to the databases at once. Points are added one by one and when number reaches batch size there are sent to server.
7884
WriteOptions& batchSize(uint16_t batchSize) { _batchSize = batchSize; return *this; }
85+
// Sets size of the write buffer to control maximum number of record to keep in case of write failures.
86+
// When max size is reached, oldest records are overwritten.
7987
WriteOptions& bufferSize(uint16_t bufferSize) { _bufferSize = bufferSize; return *this; }
88+
// Sets interval in seconds after whitch points will be written to the db. If
8089
WriteOptions& flushInterval(uint16_t flushIntervalSec) { _flushInterval = flushIntervalSec; return *this; }
90+
// Sets default retry interval in sec. This is used in case of network failure or if server is bussy and doesn't specify retry interval.
91+
// Setting to zero disables retrying.
8192
WriteOptions& retryInterval(uint16_t retryIntervalSec) { _retryInterval = retryIntervalSec; return *this; }
93+
// Sets maximum retry interval in sec.
8294
WriteOptions& maxRetryInterval(uint16_t maxRetryIntervalSec) { _maxRetryInterval = maxRetryIntervalSec; return *this; }
95+
// Sets maximum number of retry attempts of failed writes.
8396
WriteOptions& maxRetryAttempts(uint16_t maxRetryAttempts) { _maxRetryAttempts = maxRetryAttempts; return *this; }
97+
// Adds new default tag. Default tags are added to every written point.
98+
// There cannot be the same tag in the default tags and in the tags included with a point.
8499
WriteOptions& addDefaultTag(const String &name, const String &value);
100+
// Clears default tag list
85101
WriteOptions& clearDefaultTags() { _defaultTags = (char *)nullptr; return *this; }
102+
// If timestamp precision is set and useServerTimestamp is true, timestamp from point is not sent, or assigned.
103+
WriteOptions& useServerTimestamp(bool useServerTimestamp) { _useServerTimestamp = useServerTimestamp; return *this; }
86104
};
87105

88106
/**
@@ -105,7 +123,9 @@ class HTTPOptions {
105123
_connectionReuse(false),
106124
_httpReadTimeout(5000) {
107125
}
126+
// Set true if HTTP connection should be kept open. Usable for frequent writes.
108127
HTTPOptions& connectionReuse(bool connectionReuse) { _connectionReuse = connectionReuse; return *this; }
128+
// Sets timeout after which HTTP stops reading
109129
HTTPOptions& httpReadTimeout(int httpReadTimeoutMs) { _httpReadTimeout = httpReadTimeoutMs; return *this; }
110130
};
111131

src/Point.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ String Point::toLineProtocol(const String &includeTags) const {
146146
return createLineProtocol(includeTags);
147147
}
148148

149-
String Point::createLineProtocol(const String &incTags) const {
149+
String Point::createLineProtocol(const String &incTags, bool excludeTimestamp) const {
150150
String line;
151151
line.reserve(strLen(_data->measurement) + 1 + incTags.length() + 1 + _data->tags.length() + 1 + _data->fields.length() + 1 + strLen(_data->timestamp));
152152
line += _data->measurement;
@@ -162,14 +162,14 @@ String Point::createLineProtocol(const String &incTags) const {
162162
line += " ";
163163
line += _data->fields;
164164
}
165-
if(hasTime()) {
165+
if(hasTime() && !excludeTimestamp) {
166166
line += " ";
167167
line += _data->timestamp;
168168
}
169169
return line;
170170
}
171171

172-
void Point::setTime(WritePrecision precision) {
172+
void Point::setTime(WritePrecision precision) {
173173
struct timeval tv;
174174
gettimeofday(&tv, NULL);
175175

src/Point.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,6 @@ friend class InfluxDBClient;
100100
// set timestamp
101101
void setTime(char *timestamp);
102102
// Creates line protocol string
103-
String createLineProtocol(const String &incTags) const;
103+
String createLineProtocol(const String &incTags, bool excludeTimestamp = false) const;
104104
};
105105
#endif //_POINT_H_

test/Test.cpp

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ void Test::run() {
5050
testFluxTypes();
5151
testFluxTypesSerialization();
5252
testTimestampAdjustment();
53+
testUseServerTimestamp();
5354
testFluxParserEmpty();
5455
testFluxParserSingleTable();
5556
testFluxParserNilValue();
@@ -109,16 +110,10 @@ void Test::testOptions() {
109110
TEST_ASSERT(defWO._maxRetryInterval == 300);
110111
TEST_ASSERT(defWO._maxRetryAttempts == 3);
111112
TEST_ASSERT(defWO._defaultTags.length() == 0);
113+
TEST_ASSERT(!defWO._useServerTimestamp);
112114

113-
//Test max batch size
114-
// defWO = WriteOptions().batchSize(1<<14);
115-
// #if defined(ESP8266)
116-
// TEST_ASSERT(defWO._batchSize == 255);
117-
// #elif defined(ESP32)
118-
// TEST_ASSERT(defWO._batchSize == 2047);
119-
// #endif
120115

121-
defWO = WriteOptions().writePrecision(WritePrecision::NS).batchSize(32000).bufferSize(20).flushInterval(120).retryInterval(1).maxRetryInterval(20).maxRetryAttempts(5).addDefaultTag("tag1","val1").addDefaultTag("tag2","val2");
116+
defWO = WriteOptions().writePrecision(WritePrecision::NS).batchSize(32000).bufferSize(20).flushInterval(120).retryInterval(1).maxRetryInterval(20).maxRetryAttempts(5).addDefaultTag("tag1","val1").addDefaultTag("tag2","val2").useServerTimestamp(true);
122117
TEST_ASSERT(defWO._writePrecision == WritePrecision::NS);
123118
TEST_ASSERT(defWO._batchSize == 32000);
124119
TEST_ASSERT(defWO._bufferSize == 20);
@@ -127,6 +122,7 @@ void Test::testOptions() {
127122
TEST_ASSERT(defWO._maxRetryInterval == 20);
128123
TEST_ASSERT(defWO._maxRetryAttempts == 5);
129124
TEST_ASSERT(defWO._defaultTags == "tag1=val1,tag2=val2");
125+
TEST_ASSERT(defWO._useServerTimestamp);
130126

131127
HTTPOptions defHO;
132128
TEST_ASSERT(!defHO._connectionReuse);
@@ -469,7 +465,9 @@ void Test::testLineProtocol() {
469465
String testLine = "test,tag1=tagvalue fieldInt=-23i,fieldBool=true,fieldFloat1=1.12,fieldFloat2=1.12345,fieldDouble1=1.12,fieldDouble2=1.12345,fieldChar=\"A\",fieldUChar=1i,fieldUInt=23i,fieldLong=123456i,fieldULong=123456i,fieldLongLong=9123456789i,fieldULongLong=9123456789i,fieldString=\"text test\"";
470466
TEST_ASSERTM(line == testLine, line);
471467

472-
client.setWriteOptions(WriteOptions().addDefaultTag("dtag","val"));
468+
auto opts = WriteOptions().addDefaultTag("dtag","val");
469+
470+
client.setWriteOptions(opts);
473471

474472
line = client.pointToLineProtocol(p);
475473
testLine = "test,dtag=val,tag1=tagvalue fieldInt=-23i,fieldBool=true,fieldFloat1=1.12,fieldFloat2=1.12345,fieldDouble1=1.12,fieldDouble2=1.12345,fieldChar=\"A\",fieldUChar=1i,fieldUInt=23i,fieldLong=123456i,fieldULong=123456i,fieldLongLong=9123456789i,fieldULongLong=9123456789i,fieldString=\"text test\"";
@@ -542,6 +540,12 @@ void Test::testLineProtocol() {
542540
TEST_ASSERT(parts[2].length() == snow.length() + 9);
543541
delete[] parts;
544542

543+
client.setWriteOptions(opts.useServerTimestamp(true));
544+
line = client.pointToLineProtocol(p);
545+
parts = getParts(line, ' ', partsCount);
546+
TEST_ASSERT(partsCount == 2);
547+
delete [] parts;
548+
545549
TEST_END();
546550
}
547551

@@ -580,14 +584,90 @@ void Test::testBasicFunction() {
580584
TEST_ASSERTM( count == 5, String(count) + " vs 5"); //5 points
581585

582586
// test precision
583-
for (int i = (int)WritePrecision::NoTime; i <= (int)WritePrecision::NS; i++) {
587+
for (uint8_t i = (int)WritePrecision::NoTime; i <= (int)WritePrecision::NS; i++) {
584588
client.setWriteOptions((WritePrecision)i, 1);
585589
Point *p = createPoint("test1");
586590
p->addField("index", i);
587591
TEST_ASSERTM(client.writePoint(*p), String("i=") + i);
588592
delete p;
589593
}
590594

595+
596+
TEST_END();
597+
deleteAll(Test::apiUrl);
598+
}
599+
600+
bool checkLinesParts(InfluxDBClient &client, size_t lineCount, int partCount) {
601+
bool res = false;
602+
do {
603+
String query = "select";
604+
FluxQueryResult q = client.query(query);
605+
TEST_ASSERTM(!q.getError().length(), q.getError());
606+
std::vector<String> lines = getLines(q);
607+
auto count = lines.size();
608+
TEST_ASSERTM( count == lineCount, String(count) + " vs " + String(lineCount));
609+
for(size_t i=0;i<count;i++) {
610+
int partsCount;
611+
String *parts = getParts(lines[i], ',', partsCount);
612+
TEST_ASSERTM(partsCount == partCount, String(i) + ":" + lines[i]);
613+
delete[] parts;
614+
}
615+
res = true;
616+
} while(0);
617+
end:
618+
deleteAll(Test::apiUrl);
619+
return res;
620+
}
621+
622+
623+
void Test::testUseServerTimestamp() {
624+
TEST_INIT("testUseServerTimestamp");
625+
626+
InfluxDBClient client(Test::apiUrl, Test::orgName, Test::bucketName, Test::token);
627+
628+
TEST_ASSERT(waitServer(Test::managementUrl, true));
629+
630+
// test no precision, no timestamp
631+
Point *p = createPoint("test1");
632+
TEST_ASSERT(client.writePoint(*p));
633+
634+
TEST_ASSERT(checkLinesParts(client, 1, 9));
635+
636+
// Test no precision, custom timestamp
637+
auto opts = WriteOptions().batchSize(2);
638+
client.setWriteOptions(opts);
639+
640+
Point *dir = new Point("dir");
641+
dir->addTag("direction", "check-precision");
642+
dir->addTag("precision", "no");
643+
dir->addField("a","a");
644+
p->setTime("1234567890");
645+
TEST_ASSERT(client.writePoint(*dir));
646+
delete dir;
647+
TEST_ASSERT(client.writePoint(*p));
648+
649+
TEST_ASSERT(checkLinesParts(client, 1, 10));
650+
651+
// Test writerecitions + ts
652+
client.setWriteOptions(opts.writePrecision(WritePrecision::S));
653+
654+
dir = new Point("dir");
655+
dir->addTag("direction", "check-precision");
656+
dir->addTag("precision", "s");
657+
dir->addField("a","a");
658+
TEST_ASSERT(client.writePoint(*dir));
659+
TEST_ASSERT(client.writePoint(*p));
660+
661+
TEST_ASSERT(checkLinesParts(client, 1, 10));
662+
//test sending only precision
663+
client.setWriteOptions(opts.useServerTimestamp(true));
664+
665+
TEST_ASSERT(client.writePoint(*dir));
666+
TEST_ASSERT(client.writePoint(*p));
667+
delete dir;
668+
delete p;
669+
TEST_ASSERT(checkLinesParts(client, 1, 9));
670+
591671
TEST_END();
592672
deleteAll(Test::apiUrl);
593673
}
@@ -2467,7 +2547,7 @@ void Test::testLargeBatch() {
24672547
const char *line = "test1,SSID=Bonitoo-ng,deviceId=4288982576 temperature=17,humidity=28i";
24682548
uint32_t free = ESP.getFreeHeap();
24692549
#if defined(ESP8266)
2470-
int batchSize = 330;
2550+
int batchSize = 320;
24712551
#elif defined(ESP32)
24722552
// 2.0.4. introduces a memory hog which causes original 2048 lines cannot be sent
24732553
int batchSize = 1950;

test/Test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class Test : public TestBase {
4444
static void testOldAPI();
4545
static void testBatch();
4646
static void testLineProtocol();
47+
static void testUseServerTimestamp();
4748
static void testFluxTypes();
4849
static void testFluxTypesSerialization();
4950
static void testFluxParserEmpty();

test/server/server.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ app.post(prefix + '/api/v2/write', (req,res) => {
169169
console.log("Set permanentError: " + permanentError);
170170
res.status(permanentError).send("bad request");
171171
break;
172+
case 'check-precision':
173+
const precision = req.query['precision'];
174+
if(precision !== point.tags['precision'] && !(!precision && point.tags['precision']=='no')) {
175+
res.status(400).send("bad precision " + precision);
176+
}
177+
break;
172178
}
173179

174180
}

0 commit comments

Comments
 (0)