@@ -113,15 +113,38 @@ Once you're done with a row you must add a timestamp calling `at` or `at_now`.
113
113
114
114
This ordering of operations is documented for both the C and C++ APIs below.
115
115
116
- #### C function calling order
116
+ #### Buffer API
117
117
118
- ![ C API Sequential Coupling] ( api_seq/c.svg )
118
+ The ` line_sender ` object is responsible for connecting to the network and
119
+ sending data.
119
120
120
- Note that this diagram excludes error handling paths: One can call
121
- ` line_sender_close(sender) ` after any operation .
121
+ The buffer it sends is constructed separately through a ` line_sender_buffer `
122
+ object .
122
123
123
- The ` line_sender_close(sender) ` function will release memory and therefore
124
- must be called exactly once per created object.
124
+ To avoid malformed messages, this object's methods (` line_sender_buffer_* `
125
+ functions in C) must be called in a specific order.
126
+
127
+ You can accumulate multiple lines (rows) with a given buffer and a buffer is
128
+ re-usable, but a buffer may only be flushed via the sender after a ` .at() ` or
129
+ ` .at_now() ` method call (or equivalent C function).
130
+
131
+ ![ Sequential Coupling] ( api_seq/seq.svg )
132
+
133
+ #### Threading Considerations
134
+
135
+ By design, the sender and buffer objects perform all operations on the current
136
+ thread. The library will not spawn any threads internally.
137
+
138
+ By constructing multiple buffers you can design your application to build ILP
139
+ messages on multiple threads whilst handling network connectivity in a separate
140
+ part of your application on a different thread (for example by passing buffers
141
+ that need sending over a concurrent queue and sending flushed buffers back over
142
+ another queue).
143
+
144
+ Buffer and sender objects don't use any locks, so it's down to you to ensure
145
+ that a single thread owns a buffer or sender at any given point in time.
146
+
147
+ #### Error handling in the C API
125
148
126
149
In the C API, functions that can result in errors take a ` line_sender_error** `
127
150
parameter as the last argument. When calling such functions you must check the
@@ -135,9 +158,9 @@ You may then call `line_sender_error_msg(err)` and
135
158
Once handled, the error object * must* be disposed of by calling
136
159
` line_sender_error_free(err) ` .
137
160
138
- On error you must also call ` line_sender_close(sender) ` .
161
+ On error, you must also call ` line_sender_close(sender) ` .
139
162
140
- Here's a complete example on how to handle an error without leaks:
163
+ Here's a complete example of how to handle an error without leaks:
141
164
142
165
``` c
143
166
line_sender* sender = ...;
@@ -159,15 +182,7 @@ This type of error handling can get error-prone and verbose,
159
182
so you may want to use a ` goto ` to simplify handling
160
183
(see [ example] ( examples/line_sender_c_example.c ) ).
161
184
162
- #### C++ method calling order
163
-
164
- ![ C++ API Sequential Coupling] ( api_seq/cpp.svg )
165
-
166
- Note how if you're using C++, ` .close() ` can be called multiple times and will
167
- also be called automatically on object destruction.
168
-
169
- For simplicity the the diagram above does not show that the ` .close() ` method
170
- and the ` ~line_sender ` destructor at any time.
185
+ #### Error handling in the C++ API
171
186
172
187
Note that most methods in C++ may throw ` questdb::ilp::line_sender_error `
173
188
exceptions. The C++ ` line_sender_error ` type inherits from ` std::runtime_error `
0 commit comments