The Erlang way - dynamic upgrade of a server and UBF extensions

Jay Nelson jay@REDACTED
Wed Apr 30 16:25:54 CEST 2003

Joe wrote:
>   It might not have been clear in my last mail that the version was the
>version number of the contract not of the protocol.

Ah, yes.  Definitely needed, although I suppose it has to be
a triplet of UBF(A), UBF(B) and UBF(C) versions unless they
are guaranteed to all be lockstep upgraded with each version.
Enforcing the Contractv adds data to the header, but also
forces the implementer to supply one, and since it is so often
forgotten, probably a good idea.

>The client could enclose a list of IPs and the server could reply to any
>client in the list.
>   On *every* reply the server would reply with a list of servers to be
>used on  the next  call - one  the next  call the client  could choose
>*any* server.

[Vlad later added:
the versioning problem is not UBF related. The same applies to any
client-server communication. Maybe a protocol_version-negotiating protocol
is needed, if there aren't any already.]

Vlad has an interesting point that separation gives more options
and retains clarity.  I wonder if a good contract interface should
have an initial handshake that determines versions available and
exchanges a list of server IPs, as well as a directory server to
access if you lose contact.  Example:

=>  server A supports v1, 2, 4, 6;  contact IP1, IP2 or IP3 for support;
            contact IP5 for directory assistance
<=  server B supports v1, 3, 5, 6, 7; contact IP11, IP12 for support;
            contact IP15 for directory assistance

This meta-contract / meta-protocol would be exchanged on initiation
of session, or on request by either side.  If IP11 and IP12 fail (each
of which should support the same versions), the caller can access IP15
to get a new handshake with a different set of servers (presumably by
listing the failing ones and asking for other options -- which could be
answered 'none available').  Not part of UBF or the packet format, but
as a reasonable failover mechanism in the application-specific protocol.

> > <<Length:32>> = gen_tcp:recv(Socket, 4),
> > Packet = gen_tcp:recv(Socket, Length) ...
> >
>At first I thought - you're right and then I thought (later) you're wrong.
>In UBF if you get any pack of any size you just add it to the input queue,
>then you parse it byte-at-a-time until you hit $ then pop the recognition

Easy, but my initial reaction based on some projects I've been
considering is to have a "packet router".  A process that receives
the next packet, checks the type and forwards it to an appropriate
process to handle.  The router needs to get a full packet, and it
should be able to quickly analyse the data for destination determination
(i.e., check the first UBF field on the packet).
If the router has to process byte-by-byte it doesn't offer any advantage.
You would like the server to efficiently handle a heavy load and rely
on a distributed backend to keep up with the load.  Reading blocks
of data off the socket is much more efficient.

A simple compromise is to encourage a contract protocol that starts
out with a length field.

>If you want the length first you can't just output the data structure
>as it is generated - which made me think you need yet another type
>for streaming media.

(better is a unique indicator of "infinite length" and no other change)

Which is why I also thought, "no, that's wrong" after I posted the
reply.  Some applications dribble out their data as is necessary
and they are essentially an infinite stream.  But even an error logger
has a fixed size in each packet even if it dribbles out data.  If the
source was non-formatted (think of listening to morse code and
transcribing it as digital characters), you probably don't want
to receive it all as a client, then compute the size and forward it
to the server for processing.

I suspect true infinite or unformatted streamed sources are
rather rare.  As much as I find it annoying to put the size in
the Content-length tag of HTML (especially since it is in the
middle of the document, meaning you have to keep two
buffers and then append them or stream them together) it
sure makes for more efficient server options.

>A gets a SW exception at some point (a bug) - it fails and removes
>20 from its "available contracts" list.
>The session is restarted from the beginning - this time
>A can speak versions 1,2,3,4,8,12 and so a retry is
>done at level 12 (not 20).
>   Both  sides therefor  degrade to  the  level where  they think  they
>understand the protocol. (And A's error log has an entry to tell the
>programmer why level 20 failed - a contract violation).

I think this is a good approach.

>  This seems simple enough that it might work.

Now there's a goal that all designers and implementers should
aspire to!


More information about the erlang-questions mailing list