Re: [Fresco-devel] [ReFresco 03] Proposed network architecture overview

Nathaniel Smith <[email protected]> Fri, 20 Feb 2004 12:11:05 -0800
Newsgroups gmane.comp.video.fresco.devel
Message-ID <[email protected]>
(Finally replying to this, sorry it took so long; not sure how much
you still think various things, so I'll just pretend the message is
current and you can ignore comments that miss the mark.)

On Mon, Feb 02, 2004 at 02:14:10PM +0100, Tobias Hunger wrote:
> Nathaniel Smith <[email protected]> wrote:
> > > Let me do the following assumptions:
> > > 
> > >   * connections will not loose messages in transit and messages will
> > >     arrive in the same order that they were send down one connection.
> <snip>
> > Yes, we definitely assume this.
> 
> Wow, we do agree on something right on the first try! ;-)

Yay :-).

> > > Then I'd propose the following header for messages:
> 
> Aehm... make that packets, not messages! Sorry, I got confused by the
> fragmentation flag in the message... Why do you have that by the way?
> With 5byte message length field you shouldn't get into trouble there
> anytime soon.

Hrm, I _know_ I went over this, maybe on IRC?  Anyway, for
posterity...

The server is getting packets from clients A, B, C, and needs to
forward them to client F.  For safety, the server cannot queue a
packet to F until it has buffered the entire packet in memory,
because if the server were to start forwarding a packet from A and A
suddenly stopped transmitting data, the server would be stuck --
there's no way to tell F "oops, never mind, let's abort this packet in
the middle", so as a result, F would be totally starved.

So the server needs to buffer whole packets in memory; if it has a
complete packet, it can guarantee timely delivery.  However, the
server doesn't want to be forced to use arbitrarily large amounts of
memory to do so; that would be too easy a DoS.  Furthermore, it
doesn't want to send arbitrarily large packets on; they take an
arbitrarily long amount of time to transfer, and while that's
happening F can't receive other (possibly urgent) packets.

Similarly, as you mention, clients may not want to commit to sending
big huge packets that take a while to transfer; they may need to
preserve the ability to send other urgent packets in the middle, and
queuing a single giant packet will kill latency.

The solution is discretionary fragmentation: whenever the server has
decided that it's buffered as much as it's willing to, it declares
that to be a complete packet and sends it on.  Clients that want to
send a lot of data can split it into appropriately sized chunks and
send them one at a time, leaving themselves room to insert other
packets in between.

> > >    2 octects message flags:
> > >       first flag: meaning MESSAGE or REPLY
> > 
> > I don't understand what this paragraph means.  Perhaps you could
> > expand a bit on your assumptions re: the difference between messages
> > and replies?  Like, what's a "result queue"?  And why shouldn't reply
> > delivery be via a method call?  (In an asynchronous world, replies
> > always come as callbacks in one way or another...)
> 
> Having sleapt another night over ths problem: You are right. This
> flag is not necessary.

Yay again :-).

> > Not sure why you want to distinguish these cases.  I've been imagining 
> <snip> 
> > >       second flag: meaning FRAGMANT_PART or FRAGMENT_END
> > 
> > Doesn't work, because as explained below, fragment packets do need a
> > "fragment stream" identifier;
> <snip>
> 
> I am beginning to suspect that you are right here too, but I'm not sure
> yet.

How about now? ;-)

> >  Right now it's not obvious to me if there are any errors
> > to report besides "object not found",
> 
> "Connection lost", "Illegal fragment/packet", "Out of memory"
> (message is too long), ...

Are any of those reported across the network, though?  They seem like
something each client should worry about inside their own network
library, not things that we need to worry about coding in the
protocol.

Now, I have this feeling that on IRC someone suggested another error
that could occur and that would need forwarding across the network,
but I can't for the life of me remember it...

> <snip>
> > Your DOS scenario isn't a problem either.
> 
> I still think it is: Lets assume there are two object A and B in the same
> client. They share one connection to the server. A can now block that
> connection for long times by sending huge messages (the max. message length
> takes several days to go over a 100MBit network). B can't send data to the
> server during that time.
> 
> You argue that this is not an issue as it is the client's responsibility
> to look after its own latencys... I see that differently.

No, you don't see it differently, is the problem :-).  The client has
to look after its own latency either way.  In actual practice, both of
our clients end up doing the same thing -- taking packets and
splitting them up.  The difference is that you impose a particular and
fairly arbitrary policy, while I leave it to the clients/client
libraries to choose whatever they like.  Note also that your policy
doesn't even necessarily help.  If you replace:
   queue_packet(foo)
with
   for chunk in foo.split(bufsize=4096):
       queue_partial_packet(chunk)
then you haven't gained anything; you still have a single contiguous
blob of data queued, it just has some extra headers stuck in the
middle of it now.  To actually get an advantage, you have to do some
special handling where you don't _actually_ queue any fragment until
you know that the previous one has actually reached the wire, etc.,
and this complexity doesn't care which solution we use.

Worse, the previous can be hidden in the network library, but in
general things can't be; the client itself has to care, because
fragmenting packets like this changes ordering semantics.  If I do
  send_message(foo); send_message(bar)
correct behavior really demands that foo be received before bar; only
the client knows if this constraint can be relaxed in a particular
case, and has to do something to say so.  (Maybe have a special
send_eventually() library call that does what I described in the
previous paragraph with a separate queue and all.)

> <snip>
> > > A fragment stream field is not necessary: Under the assumptions made above
> > > the packages arrive in order. One object will of course not sprinckle other
> > > communication inbetween one message it is sending, so there's no danger of
> > > a mix up there either.
> > 
> > No, this is wrong.  Say we have
> > 
> >    A --\
> >         Server --- C
> >    B --/
> > 
> > A and B both start sending long messages to C.
> <snip>
> > So as soon as the server is unwilling to buffer any more,
> > it starts fragmenting the messages from A and B, and sending the
> > fragments to C.  These fragments will arrive intermixed.
> 
> ... which is not a problem as long as they were send by two separate
> originators. That's basically a stream number... of course this is
> assuming that a originator will be set, even on asynchronous messages
> which is not the case with the architecture you are proposiing. Mine
> is different in that respect.

I'm really not sure I understand this.  Perhaps by "originator" you
mean the cap included for error delivery?  That's not true; e.g., I
expect lots of messages to have the null cap as their errors-to
address, meaning, "I don't care if this goes through or not".  (Most
method replies, one-way callbacks, etc. have this property.)

> <snip>
> > > > > I still fail to see why we don't need sequence numbers. Is the cap thrown
> > > > > away after receiving a single message?
> 
> Of course it is! I got it now!

Yay three times! :-)

-- Nathaniel

-- 
.i dei jitfa fanmo xatra