Skip to content

Commit 9154fd9

Browse files
committed
docs: Improved section describing the protocol
1 parent 3ad3bd0 commit 9154fd9

File tree

6 files changed

+98
-36
lines changed

6 files changed

+98
-36
lines changed

BUILD.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Visual Studio Code should also work well provided you have the "C/C++",
3737

3838
## Build outputs
3939

40-
The build will generate libraries compiled to `.build/`
40+
The build will generate libraries compiled to `./build`
4141
(or your otherwise selected build directory).
4242

4343
You will find one dynamic library and depending on

README.md

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# QuestDB - Line Protocol - Ingestion Client Library for C and C++
1+
# c-questdb-client
2+
**QuestDB - InfluxDB Line Protocol - Ingestion Client Library for C and C++**
23

34
This library makes it easy to insert data into [QuestDB](https://questdb.io/).
45

@@ -16,7 +17,7 @@ to get notified of new releases.
1617

1718
## Protocol
1819

19-
This client library implements the input line protocol (ILP) over TCP.
20+
This client library implements the InfluxDB Line Protocol (ILP) over TCP.
2021

2122
To understand the benefits and limitations of using the protocol, you may want
2223
to consult the
@@ -27,24 +28,27 @@ it can't report errors that happen in the server as the protocol is not designed
2728
to report them.
2829

2930
For example, the client library will report that a supplied string isn't encoded
30-
in UTF-8, but will not report that a column previously used to record `BOOLEAN`
31-
data in a previous row is used in a later row to record a `STRING`.
31+
in UTF-8, but will not report that a column previously used to record a
32+
`BOOLEAN` is later incorrectly used to record a `STRING`.
3233

33-
Unreported errors such as this one usually result in the row not being written
34-
and skipped silently to the client.
34+
Unreported errors such as this one usually result in skipped rows that don't
35+
get saved.
3536

36-
Other errors, such as the database running out of disk space or crashing, mean
37-
that.
37+
Other errors, such as the database running out of disk space or crashing will
38+
result in a TCP connection drop, but also uncertainty from the client's
39+
perspective on which records were written.
3840

3941
With these caveats out of the way, you can however expect well-formed data to be
40-
written so long as it has been received by the database.
42+
written so long as it has been received by the database, caveat bugs and system
43+
errors.
4144

42-
The input line protocol is currently the fastest way to insert data into
45+
The line protocol is currently the fastest way to insert data into
4346
QuestDB, but if the lack of confirmation of written data is a concern to you,
4447
you may want to consider alternatives such as inserting via CSV uploads or
45-
through the PostgreSQL interface.
48+
through the PostgreSQL interface. Those alternatives are slower but more robust.
49+
The choice of input mechanism will depend on your use case.
4650

47-
When using the input line protocol, data will be written in the order it is
51+
When using the line protocol, data will be written in the order it is
4852
sent, unless you configured the
4953
[out-of-order feature](https://questdb.io/docs/guides/out-of-order-commit-lag/#how-to-configure-out-of-order-ingestion)
5054
in QuestDB which will reorder records based on timestamp for a given insertion
@@ -64,13 +68,40 @@ silently resulting in rows not getting inserted.
6468
`,`, `'`, `"`, `\`, `/`, `:`, `(`, `)`, `+`, `-`, `*`, `%`, `~`,
6569
`' '` (space), `\0` (nul terminator),
6670
[ZERO WIDTH NO-BREAK SPACE](https://unicode-explorer.com/c/FEFF).
71+
* Each row should contain, *in order*:
72+
* table name
73+
* at least one of:
74+
* symbols, zero or more
75+
* columns, zero or more
76+
* timestamp, optionally
6777

6878
### Non-validated rules
6979

70-
TODO: document me.
80+
The following is a non-exhaustive of guidelines to follow:
7181

72-
* Different types across rows.
73-
* Reused column names and symbols.
82+
* For a given row, a column name should not be repeated.
83+
If it's repeated, only the first value will be kept.
84+
This also applies to symbols.
85+
* Values for a given colum should always have the same type.
86+
If changing types the whole row will be dropped (unless we can cast).
87+
* If supplying timestamps these need to be at least equal to
88+
previous ones in the same table, unless using the out of order
89+
feature. Such rows would be dropped.
90+
* The timestamp column should be written out through the provided
91+
`line_sender_at` function (in C) or or `.at()` method in (C++).
92+
It is also possible to write out additional timestamps values
93+
as columns.
94+
95+
*Refer to the
96+
[protocol reference docs](https://questdb.io/docs/reference/api/ilp/overview/)
97+
for details and more usage guidelines.*
98+
99+
### Data type conversions
100+
101+
The ILP protocol has its own set of data types which is smaller
102+
that the set supported by QuestDB.
103+
We map these types into QuestDB types and perform conversions
104+
as necessary wherever possible.
74105

75106
## Building this library
76107

@@ -203,6 +234,19 @@ and you can obtain an error message description by calling `.what()`.
203234
If you intend to retry, you must create a new sender object: The same sender
204235
object can't be reused.
205236

237+
## Symbols or Strings
238+
239+
SYMBOLs are strings with which are automatically
240+
[interned](https://en.wikipedia.org/wiki/String_interning) by the database on a
241+
per-column basis.
242+
You should use this type if you expect the string to be re-used over and over.
243+
This is common for identifiers, etc.
244+
245+
For one-off strings use STRING columns.
246+
247+
For more details see our
248+
[datatypes](https://questdb.io/docs/reference/sql/datatypes) page.
249+
206250
## If you don't see any data
207251

208252
You may be experiencing one of these three issues.
@@ -211,7 +255,7 @@ You may be experiencing one of these three issues.
211255

212256
If you can't initially see your data through a `select` query straight away,
213257
his is normal: by default the database will only commit data it receives
214-
though the input line protocol periodically to maximize throughput.
258+
though the line protocol periodically to maximize throughput.
215259

216260
For dev/testing you may want to tune the following database configuration
217261
parameters as so:

TODO.md

Lines changed: 0 additions & 10 deletions
This file was deleted.

include/questdb/line_sender.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ bool line_sender_name_init(
146146
/////////// Connecting and disconnecting.
147147

148148
/**
149-
* Insert data into QuestDB via the input line protocol.
149+
* Insert data into QuestDB via the InfluxDB Line Protocol.
150150
*
151151
* Batch up rows, then call `line_sender_flush` to send.
152152
*/

include/questdb/line_sender.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ namespace questdb
182182
}
183183

184184
/**
185-
* Insert data into QuestDB via the input line protocol.
185+
* Insert data into QuestDB via the InfluxDB Line Protocol.
186186
*
187187
* Batch up rows, then call `.flush()` to send.
188188
*/

system_test/test.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,15 @@ def check_table():
8787
return retry(check_table, timeout_sec=timeout_sec)
8888

8989

90-
class TestSomething(unittest.TestCase):
90+
def ns_to_qdb_date(at_ts_ns):
91+
# We first need to match QuestDB's internal microsecond resolution.
92+
at_ts_us = int(at_ts_ns / 1000.0)
93+
at_ts_sec = at_ts_us / 1000000.0
94+
at_td = datetime.datetime.fromtimestamp(at_ts_sec)
95+
return at_td.isoformat() + 'Z'
96+
97+
98+
class TestLineSender(unittest.TestCase):
9199
def test_insert_three_rows(self):
92100
table_name = uuid.uuid4().hex
93101
with qls.LineSender('localhost', QDB_FIXTURE.line_tcp_port) as sender:
@@ -244,16 +252,36 @@ def test_at(self):
244252
.symbol('a', 'A')
245253
.at(at_ts_ns))
246254

247-
# We first need to match QuestDB's internal microsecond resolution.
248-
at_ts_us = int(at_ts_ns / 1000.0)
249-
at_ts_sec = at_ts_us / 1000000.0
250-
at_td = datetime.datetime.fromtimestamp(at_ts_sec)
251-
at_str = at_td.isoformat() + 'Z'
255+
resp = retry_check_table(table_name)
256+
exp_dataset = [['A', ns_to_qdb_date(at_ts_ns)]]
257+
self.assertEqual(resp['dataset'], exp_dataset)
258+
259+
def test_bad_at(self):
260+
if QDB_FIXTURE.version <= (6, 0, 7, 1):
261+
self.skipTest('No support for user-provided timestamps.')
262+
return
263+
table_name = uuid.uuid4().hex
264+
at_ts_ns1 = 1648032959100000000
265+
at_ts_ns2 = 1648032958100000000 # A second before `at_ts_ns1`.
266+
with qls.LineSender('localhost', QDB_FIXTURE.line_tcp_port) as sender:
267+
(sender
268+
.table(table_name)
269+
.symbol('a', 'A')
270+
.at(at_ts_ns1))
271+
(sender
272+
.table(table_name)
273+
.symbol('a', 'B')
274+
.at(at_ts_ns2))
252275

253276
resp = retry_check_table(table_name)
254-
exp_dataset = [['A', at_str]]
277+
exp_dataset = [['A', ns_to_qdb_date(at_ts_ns1)]]
255278
self.assertEqual(resp['dataset'], exp_dataset)
256279

280+
# The second time stamp is dropped and will not appear in results.
281+
with self.assertRaises(TimeoutError):
282+
retry_check_table(table_name, min_rows=2, timeout_sec=1)
283+
284+
257285
def test_underscores(self):
258286
table_name = f'_{uuid.uuid4().hex}_'
259287
with qls.LineSender('localhost', QDB_FIXTURE.line_tcp_port) as sender:

0 commit comments

Comments
 (0)