@@ -113,15 +113,38 @@ Once you're done with a row you must add a timestamp calling `at` or `at_now`.
113113
114114This ordering of operations is documented for both the C and C++ APIs below.
115115
116- #### C function calling order
116+ #### Buffer API
117117
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.
119120
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 .
122123
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
125148
126149In the C API, functions that can result in errors take a ` line_sender_error** `
127150parameter 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
135158Once handled, the error object * must* be disposed of by calling
136159` line_sender_error_free(err) ` .
137160
138- On error you must also call ` line_sender_close(sender) ` .
161+ On error, you must also call ` line_sender_close(sender) ` .
139162
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:
141164
142165``` c
143166line_sender* sender = ...;
@@ -159,15 +182,7 @@ This type of error handling can get error-prone and verbose,
159182so you may want to use a ` goto ` to simplify handling
160183(see [ example] ( examples/line_sender_c_example.c ) ).
161184
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
171186
172187Note that most methods in C++ may throw ` questdb::ilp::line_sender_error `
173188exceptions. The C++ ` line_sender_error ` type inherits from ` std::runtime_error `
0 commit comments