Re: Stdio.Buffer.set_buffer_mode revisited
"Stephen R. van den Berg" <[email protected]>
| Newsgroups | gmane.comp.lang.pike.user |
|---|---|
| Message-ID | <[email protected]> |
Per Hedbor () @ Pike (-) importm?te f?r mailinglistan wrote:
>Per Hedbor () @ Pike (-) importm?te f?r mailinglistan wrote:
>> :-). Well, it was because it originated from code that used a single
>> socket to read/write. I guess I could separate it now.
>It would still be a single socket, just two explicit buffer objects.
Yes, well, for a brief moment I had a version which had a second
Buffer as a member object in conxion, then I realised it would cost
me an indirection every time. So basically it was a result of not
wanting to pay the price of the extra indirection. I can only guess
that Pike does not make me pay that price behind the scenes because it
has to resolve the overridden functions.
>It should simplify the code a bit, really. There is no real need to
>have the two objects be aggregated in one, writing data to one is not
>really related to reading from the other.
Correct. Well, I'll look into separating them, if only to avoid
future problems with Buffer objects starting to interact with each other.
>>> See also: All your wrappers for the various read_* functions.
>> Is there anything amiss with those? I thought that was straightforward.
>Well, yes, but they would not be needed at all if there was a separate
>input and output buffer object.
True. But do they incur a runtime cost compared to non-overridden ones?
>> In order to reduce network traffic, reduce latency and increase
>> throughput, I need to decide when to flush the network buffer (I end
>> up with too many too small packets otherwise). So the current
>> version of the automatic output buffer flushing is unsuitable. I'd
>> love one where I can decide how much can be flushed.
>Hm, well, that could be implemented. Currently it will flush when the
>code returns to the backend (that is, much the same as when using
>'normal' write callback based I/O).
That won't work for me, since I need the backend to keep running
to collect extra input, while I'm still assembling the output
(and the output is not allowed to leave yet). This would mean that
I would have to use a separate output backend to get this currently.
Adding this functionality to Buffer would be appreciated.
>>>o There is no need to have a write_cb, really, it should be
>>> pointless. Writing is handled by the file object in buffered mode.
>> That's just it, I cannot use the buffered mode due to too small packaging
>> on the network side.
>This is somewhat surpising, but I assume it is because data is written
>to the buffer from a different thread than the backend one?
Correct. The backend is a local backend, which runs even if the
standard backend does not run, because the connection with the database
is fully event driven and needs to run independent of any
regular backend running or not.
>In that case I can see why it could cause a lot of small writes, since
>the backend is free to write the data you are just adding to the buffer.
Quite.
>However, is there any need to write the data from another thread in
>the "normal" case?
I started to describe what the normal cases are, but thing is, the
network protocol employed by the database has:
- Between two and six command packets per query start.
- Between one and three command replies per query start.
- One reply packet per row returned.
- Rows come in sets.
- Sets of different queries might be interleaved.
- The command packets can be sent in parallel/interleaved for different
queries.
- The replies are mostly "unmarked", i.e. you need to remember which
commands you sent, the replies will come in the same order, but delayed
by an arbitrary amount of time.
So, we're multiplexing commands and queries and results over the same
connection. In order to make it work, the semantics of the protocol
need to be observed, i.e. some things have to wait until others have
happened. So it's one happy multiplexing mess of a statemachine that
sometimes throws a mutex to have things wait, but never longer than necessary.
The occasional mutex means that writer and reader need to be asynchronous
and decoupled, and that means that imposing any traditional neatly
ordered backend will be difficult.
>> c->read_int8() and c->read_hstring()
>> and I want the call to block and wait.
>In that case you would need the mutex+cond, yes.
>Personally I would have written the code to be nonblocking at that
>level, and do the blocking stuff at a level where it is easier
>(in the query method, as an example).
That's what the old driver did. It is easier (a lot), but it has a higher
latency in communication with the server and reduced parallelism.
The current driver supports things like:
db=Sql.Sql("pgsql://...."); // returns immediately because the connect
// and handshake happens in the background
res1=db->big_query("SELECT 1..."); // returns immediately before the query
// has even delivered the first row
res2=db->big_query("SELECT 2..."); // As above
res3=db->big_query("SELECT 3..."); // As above
// The following statements will fetch rows from
// the appropriate queries as soon as the row arrives
// from the database; the resultset is never prefetched
// from the database in full. The driver dynamically
// determines how many resultrows to prefetch
res3->fetch_row();
res2->fetch_row();
res1->fetch_row();
res2->fetch_row();
res3->fetch_row();
--
Stephen.