Skip to content

Commit 9f4aafa

Browse files
mtopolnikjerrinotkafka1991bluestreak01amunra
authored
Document N-dimensional arrays (#160)
Co-authored-by: Jaromir Hamala <jaromir.hamala@gmail.com> Co-authored-by: victor <vicgao@tencent.com> Co-authored-by: victor <victorgaoyh@gmail.com> Co-authored-by: Vlad Ilyushchenko <bluestreak01@users.noreply.github.com> Co-authored-by: Adam Cimarosti <cimarosti@gmail.com>
1 parent 9c990fd commit 9f4aafa

File tree

21 files changed

+2718
-617
lines changed

21 files changed

+2718
-617
lines changed

documentation/clients/ingest-c-and-cpp.md

Lines changed: 334 additions & 55 deletions
Large diffs are not rendered by default.

documentation/clients/ingest-dotnet.md

Lines changed: 111 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ perform basic insert operations.
2929

3030
:::info
3131

32-
This page focuses on our high-performance ingestion client, which is optimized for **writing** data to QuestDB.
33-
For retrieving data, we recommend using a [PostgreSQL-compatible .NET library](/docs/pgwire/c-sharp/) or our
32+
This page focuses on our high-performance ingestion client, which is optimized
33+
for **writing** data to QuestDB. For retrieving data, we recommend using a
34+
[PostgreSQL-compatible .NET library](/docs/pgwire/c-sharp/) or our
3435
[HTTP query endpoint](/docs/reference/sql/overview/#rest-http-api).
3536

3637
:::
3738

38-
3939
## Requirements
4040

4141
- .NET 6.0 or higher is required.
@@ -59,7 +59,7 @@ dotnet add package net-questdb-client
5959
`Sender` is single-threaded, and uses a single connection to the database.
6060

6161
If you want to send in parallel, you can use multiple senders and standard async
62-
tasking.
62+
tasks.
6363

6464
:::
6565

@@ -97,11 +97,11 @@ using var sender = Sender.New("http::addr=localhost:9000;username=admin;token=<t
9797
TCP authentication can be configured using JWK tokens:
9898

9999
```csharp
100-
using var sender = Sender.New("tcp::addr=localhost:9000;username=admin;token=<token>");
100+
using var sender = Sender.New("tcp::addr=localhost:9009;username=admin;token=<token>");
101101
```
102102

103-
The connection string can also be built programatically. See
104-
[Configuration](#configuration) for details.
103+
The connection options can also be built programatically. See
104+
[Ways to create the client](#ways-to-create-the-client) for details.
105105

106106
## Basic insert
107107

@@ -127,8 +127,8 @@ await sender.Table("trades")
127127
await sender.SendAsync();
128128
```
129129

130-
In this case, the designated timestamp will be the one at execution time. Let's
131-
see now an example with timestamps, custom auto-flushing, basic auth, and error
130+
In this case, we asked the server to assign the timestamp to each row. Let's see
131+
now an example with timestamps, custom auto-flushing, basic auth, and error
132132
reporting.
133133

134134
```csharp
@@ -140,7 +140,9 @@ class Program
140140
{
141141
static async Task Main(string[] args)
142142
{
143-
using var sender = Sender.New("http::addr=localhost:9000;username=admin;password=quest;auto_flush_rows=100;auto_flush_interval=1000;");
143+
using var sender = Sender.New(
144+
"http::addr=localhost:9000;username=admin;password=quest;auto_flush_rows=100;auto_flush_interval=1000;"
145+
);
144146

145147
var now = DateTime.UtcNow;
146148
try
@@ -171,81 +173,94 @@ class Program
171173
}
172174
```
173175

174-
As you can see, both events use the same timestamp. We recommended using the
175-
original event timestamps when ingesting data into QuestDB. Using the current
176-
timestamp hinder the ability to deduplicate rows which is
176+
Now, both events use the same timestamp. We recommend using the event's
177+
original timestamp when ingesting data into QuestDB. Using ingestion-time
178+
timestamps precludes the ability to deduplicate rows, which is
177179
[important for exactly-once processing](/docs/reference/api/ilp/overview/#exactly-once-delivery-vs-at-least-once-delivery).
178180

179-
## Configuration
181+
## Ways to create the client
180182

181-
Construct new Senders via the `Sender` factory.
183+
There are three ways to create a client instance:
182184

183-
It is mandatory to provide the `addr` config, as this defines the transport
184-
protocol and the server location.
185+
1. **From a configuration string.** This is the most common way to create a
186+
client instance. It describes the entire client configuration in a single
187+
string. See [Configuration options](#configuration-options) for all available
188+
options. It allows sharing the same configuration across clients in different
189+
languages.
185190

186-
By default, the HTTP protocol uses `9000`, the same as the other HTTP endpoints.
187-
Optionally, TCP uses `9009`.
191+
```csharp
192+
using var sender = Sender.New("http::addr=localhost:9000;");
193+
```
188194

189-
### With a configuration string
195+
2. **From an environment variable.** The `QDB_CLIENT_CONF` environment variable
196+
is used to set the configuration string. Moving configuration parameters to
197+
an environment variable allows you to avoid hard-coding sensitive information
198+
such as tokens and password in your code.
190199

191-
It is recommended, where possible, to initialise the sender using a
192-
[configuration string](/docs/configuration-string/).
200+
If you want to initialise some properties programmatically after the initial
201+
config string, you can use `Configure` and `Build`.
193202

194-
Configuration strings provide a convenient shorthand for defining client
195-
properties, and are validated during construction of the `Sender`.
203+
```bash
204+
export QDB_CLIENT_CONF="http::addr=localhost:9000;auto_flush_rows=5000;retry_timeout=10000;"
205+
```
196206

197-
```csharp
198-
using var sender = Sender.New("http::addr=localhost:9000;");
199-
```
207+
```csharp
208+
(Sender.Configure("http::addr=localhost:9000;") with { auto_flush = AutoFlushType.off }).Build()
209+
```
200210

201-
If you want to initialise some properties programmatically after the initial
202-
config string, you can use `Configure` and `Build`.
211+
3. **From SenderOptions.**
203212

204-
```csharp
205-
(Sender.Configure("http::addr=localhost:9000;") with { auto_flush = AutoFlushType.off }).Build()
206-
```
213+
```csharp
214+
await using var sender = Sender.New(new SenderOptions());
215+
```
207216

208-
### From options
217+
This way you can bind options from configuration:
209218

210-
The sender API also supports construction from `SenderOptions`.
219+
```json
220+
{
221+
"QuestDB": {
222+
"addr": "localhost:9000",
223+
"tls_verify": "unsafe_off;"
224+
}
225+
}
226+
```
211227

212-
```csharp
213-
await using var sender = Sender.New(new SenderOptions());
214-
```
228+
```csharp
229+
var options = new ConfigurationBuilder()
230+
.AddJsonFile("config.json")
231+
.Build()
232+
.GetSection("QuestDB")
233+
.Get<SenderOptions>();
234+
```
215235

216-
You might use this when binding options from configuration:
236+
## Configuration options
217237

218-
```json
219-
{
220-
"QuestDB": {
221-
"addr": "localhost:9000",
222-
"tls_verify": "unsafe_off;"
223-
}
224-
}
238+
The easiest way to configure the `Sender` is the configuration string. The
239+
general structure is:
240+
```plain
241+
<transport>::addr=host:port;param1=val1;param2=val2;...
225242
```
243+
`transport` can be `http`, `https`, `tcp`, or `tcps`. Go to the client's
244+
[crate documentation](https://docs.rs/questdb-rs/latest/questdb/ingress) for the
245+
full details on configuration.
246+
Alternatively, for breakdown of available params, see the
247+
[Configuration string](/docs/configuration-string/) page.
226248

227-
```csharp
228-
var options = new ConfigurationBuilder()
229-
.AddJsonFile("config.json")
230-
.Build()
231-
.GetSection("QuestDB")
232-
.Get<SenderOptions>();
233-
```
234249

235250
## Preparing Data
236251

237-
Senders use an internal buffer to convert input values into an ILP-compatible
238-
UTF-8 byte-string.
252+
The Sender uses an internal buffer to convert input values into an
253+
ILP-compatible UTF-8 byte-string.
239254

240-
This buffer can be controlled using the `init_buf_size` and `max_buf_size`
255+
You can control buffer sizing with the `init_buf_size` and `max_buf_size`
241256
parameters.
242257

243258
Here is how to build a buffer of rows ready to be sent to QuestDB.
244259

245260
:::warning
246261

247262
The senders are **not** thread safe, since they manage an internal buffer. If
248-
you wish to send data in parallel, you should construct multiple senders and use
263+
you wish to send data in parallel, construct multiple senders and use
249264
non-blocking I/O to submit to QuestDB.
250265

251266
:::
@@ -281,44 +296,46 @@ The table name must always be called before other builder functions.
281296
### Add symbols
282297

283298
A [symbol](/docs/concept/symbol/) is a dictionary-encoded string, used to
284-
efficiently store commonly repeated data. This is frequently used for
285-
identifiers, and symbol columns can have
286-
[secondary indexes](/docs/concept/indexes/) defined upon them.
299+
efficiently store commonly repeated data. We recommend using this type for
300+
identifiers, because you can create a
301+
[secondary index](/docs/concept/indexes/) for a symbol column.
287302

288-
Symbols can be added using calls to `Symbol`, which expects a symbol column
289-
name, and string value.
303+
Add symbols by calling `Symbol()`, which expects a symbol column
304+
name, and a string value.
290305

291306
```csharp
292307
sender.Symbol("foo", "bah");
293308
```
294309

295-
All symbol columns must be defined before any other column definition.
310+
You must specify all symbol columns first, before any other columns.
296311

297312
### Add other columns
298313

299-
A number of data types can be submitted to QuestDB via ILP, including string /
314+
There are several data types you can send to QuestDB via ILP, including string /
300315
long / double / DateTime / DateTimeOffset.
301316

302-
These can be written using the `Column` functions.
317+
Provide these by calling `Column()`.
303318

304319
```csharp
305320
sender.Column("baz", 102);
306321
```
307322

308323
### Finish the row
309324

310-
A row is completed by defining the designated timestamp value:
325+
Completed a row by specifying the designated timestamp:
311326

312327
```csharp
313328
sender.At(DateTime.UtcNow);
314329
```
315330

316-
Generation of the timestamp can be offloaded to the server, using `AtNow`.
331+
You can also let the server assign the timestamp, by calling `AtNow()` instead.
317332

318333
:::caution
319334

320-
Using a server generated timestamp via AtNow/AtNowAsync is not compatible with
321-
QuestDB's deduplication feature, and should be avoided where possible.
335+
We recommend using the event's original timestamp when ingesting data into
336+
QuestDB. Using ingestion-time timestamps precludes the ability to deduplicate
337+
rows, which is
338+
[important for exactly-once processing](/docs/reference/api/ilp/overview/#exactly-once-delivery-vs-at-least-once-delivery).
322339

323340
:::
324341

@@ -329,16 +346,15 @@ database automatically, or manually.
329346

330347
### Auto-flushing
331348

332-
When the `At` functions are called, the auto-flushing parameters are checked to
333-
see if it is appropriate to flush the buffer. If an auto-flush is triggered,
334-
data will be sent to QuestDB.
349+
When you call one of the `At` functions, the row is complete. The sender checks
350+
the auto-flushing parameters to see if it should flush the buffer to the server.
335351

336352
```csharp
337353
sender.At(new DateTime(0,0,1));
338354
```
339355

340-
To avoid blocking the calling thread, one can use the Async overloads of the
341-
`At`. functions e.g `AtAsync`.
356+
To avoid blocking the calling thread, use the Async overloads of the `At`, such
357+
as `AtAsync`.
342358

343359
```csharp
344360
await sender.AtNowAsync();
@@ -352,54 +368,52 @@ using var sender = Sender.New("http::addr=localhost:9000;auto_flush=off;"); // o
352368

353369
#### Flush by rows
354370

355-
Users can specify a threshold of rows to flush. This is effectively a submission
356-
batch size by number of rows.
371+
You can specify the number of rows that will trigger an auto-flush, creating a
372+
batch insert operation of that size.
357373

358374
```csharp
359375
using var sender = Sender.New("http::addr=localhost:9000;auto_flush=on;auto_flush_rows=5000;");
360376
```
361377

362-
By default, HTTP senders will send after `75,000` rows, and TCP after `600`
378+
By default, the HTTP sender auto-flushes after 75,000 rows, and TCP after 600
363379
rows.
364380

365381
:::tip
366382

367383
`auto_flush_rows` and `auto_flush_interval` are both enabled by default. If you
368-
wish to only auto-flush based on one of these properties, you can disable the
369-
other using `off` or `-1`.
384+
wish to only auto-flush based on one of these properties, disable the other
385+
using `off` or `-1`.
370386

371387
:::
372388

373389
#### Flush by interval
374390

375-
Specify a time interval between batches. This is the elapsed time from the last
376-
flush, and is checked when the `At` functions are called.
391+
You can specify the time interval between auto-flushes. The sender checks it
392+
every time you call an `At` function.
377393

378394
```csharp
379395
using var sender = Sender.New("http::addr=localhost:9000;auto_flush=on;auto_flush_interval=5000;");
380396
```
381397

382-
By default, `auto_flush_interval` is set to `1000` ms.
398+
By default, `auto_flush_interval` is 1000 ms.
383399

384400
#### Flush by bytes
385401

386-
Specify a buffer length after which to flush, effectively a batch size in UTF-8
387-
bytes. This should be set according to `init_buf_size` $\lt$ `auto_flush_bytes`
388-
$\leq$ `max_buf_size`.
402+
As an additional option, disabled by default, you can specify the batch size in
403+
terms of bytes instead of rows. You should ensure that `init_buf_size`
404+
$\lt$ `auto_flush_bytes` $\leq$ `max_buf_size`.
389405

390-
This can be useful if a user has variety in their row sizes and wants to limit
391-
the request sizes.
406+
This can be useful if you have large variation in row sizes and want to limit
407+
the request sizes. By default, this is disabled, but set to `100 KiB`.
392408

393409
```csharp
394410
using var sender = Sender.New("http::addr=localhost:9000;auto_flush=on;auto_flush_bytes=65536;");
395411
```
396412

397-
By default, this is disabled, but set to `100 KiB`.
398-
399413
### Explicit flushing
400414

401-
Manually flush the buffer using `Send`. and `SendAsync`. This will send any
402-
outstanding data to the QuestDB server.
415+
You can also manually flush the buffer at any time by calling `Send` or
416+
`SendAsync`. This will send any outstanding data to the QuestDB server.
403417

404418
```csharp
405419
using var sender = Sender.New("http::addr=localhost:9000;auto_flush=off;");
@@ -409,8 +423,10 @@ await sender.SendAsync(); // send non-blocking
409423
sender.Send(); // send synchronously
410424
```
411425

412-
It is recommended to always end your submission code with a manual flush. This
413-
will ensure that all data has been sent before disposing of the Sender.
426+
You should always perform an explicit flush before closing the sender. The HTTP
427+
sender normally retries the requests in case of errors, but won't do that while
428+
auto-flushing before closing. Flushing explicitly ensures that the client
429+
applies the same effort to send all the remaining data.
414430

415431
## Transactions
416432

@@ -478,7 +494,8 @@ Add data to a transaction in the usual way, but without calling `Table` between
478494
rows.
479495

480496
```csharp
481-
sender.Symbol("bah", "baz").Column("num", 123).At(DateTime.UtcNow); // adds a symbol, integer column, and ends with current timestamp
497+
// add a symbol, integer column, and end with current timestamp
498+
sender.Symbol("bah", "baz").Column("num", 123).At(DateTime.UtcNow);
482499
```
483500

484501
### Closing a transaction
@@ -498,7 +515,7 @@ server.
498515
sender.Rollback();
499516
```
500517

501-
## Misc.
518+
## Misc
502519

503520
### Cancelling rows
504521

@@ -581,7 +598,6 @@ using var sender =
581598
Sender.New(
582599
"tcps::addr=localhost:9009;tls_verify=unsafe_off;username=admin;token=NgdiOWDoQNUP18WOnb1xkkEG5TzPYMda5SiUOvT1K0U=;");
583600
// See: /docs/reference/api/ilp/authenticate
584-
585601
```
586602

587603
## Next Steps

0 commit comments

Comments
 (0)