On Mon, Apr 8, 2013 at 1:45 PM, Michael Goulish <mgoul...@redhat.com> wrote:

>
> While working on docs, I was getting excited about something cool that I
> could do with pn_messenger_route() ----
> And then I realized that I couldn't.
>
> What would you think about allowing replacement of an old route by a new
> route with the same pattern ?
>
> It seems like this would allow some cool, adaptive behavior in Proton
> networks.
>
> I just coded and tested a simple case successfully -- sending 2 messages
> to an abstract address, and having them go to 2 different receivers because
> a new route got set in between.
>
> Would this behavior cause any problems that would outweigh the coolness?
>

Adaptive behaviour is definitely something I would like to support, however
I've been planning on doing this a bit differently. Currently a routing
rule is of the form <pattern> -> <next-hop>. Imagine extending this so that
there could be multiple next hops, e.g. you could specify a rule like so:
<pattern> -> <next-hop1>, <next-hop2>, ... <next-hopN>

Now you might wonder what multiple next hops actually means in terms of
semantics, and there are probably many possibilities for this, but I can
think of three generally useful ones to start. The first being redundant
alternatives. If you lose connectivity to one next-hop, you have N-1 others
to choose from. The second option might be load balancing, e.g. messages
that match this pattern should be randomly distributed or round-robined. A
third option might just be broadcast, send the message to all next hops.

So let's imagine extending the rule syntax a bit more, e.g. you could
specify any of the following:

  <pattern> -> <next-hop>    // current style
  <pattern> -> failover(<next-hop1>, ..., <next-hopN>)    // ask for
failover behaviour
  <pattern> -> balance(<next-hop1>, ..., <next-hopN>)    // ask for load
balancing
  <pattern> -> broadcast(<next-hop1>, ..., <next-hopN>)    // ask for
broadcast

You can imagine other semantics that might make sense to add, but just
these basic building blocks would provide a fairly powerful basis for
building different kinds of dynamically adaptive networks.

The key difference here from what you are describing is that the routes are
not manually altered by the application code, but rather the adaptive
behaviour is described in configuration. I think this is more in keeping
with the topology neutral aspect of the applications interface to the
proton API. In other words the application doesn't have to code its dynamic
routing behaviour, the application simply assumes messages magically
come/go from/to all the right places, and orthogonal to this you can
configure what "the right place" is.

That said, I don't think being able to programmaticaly delete/alter the
routing configuration is a bad thing. I just think you should be able to
express some of the stuff I described above in a simple static
configuration file and have messenger magically provide the dynamic
semantics underneath. This provides that clean separation between topology
and application logic that we are trying to achieve.


>
> Code seems pretty straightforward - - - -
>

>
> int pn_messenger_route(pn_messenger_t *messenger, const char *pattern,
> const char *address)
> {
>   if (strlen(pattern) > PN_MAX_PATTERN || strlen(address) > PN_MAX_ROUTE) {
>     return PN_ERR;
>   }
>   pn_route_t *new_route = (pn_route_t *) malloc(sizeof(pn_route_t));
>   if (!new_route) return PN_ERR;
>
>   strcpy(new_route->pattern, pattern);
>   strcpy(new_route->address, address);
>   new_route->next = NULL;
>
>   /* The list is empty. */
>   if (! messenger->routes ) {
>     messenger->routes = new_route;
>     return 0;
>   }
>
>   pn_route_t *old;
>
>   /* The route to be replaced is first on the list. */
>   if ( ! strcmp ( messenger->routes->pattern, new_route->pattern ) ) {
>     old = messenger->routes;
>     new_route->next = old->next;
>     messenger->routes = new_route;
>     free ( (char *) old );
>     return 0;
>   }
>
>   pn_route_t *route = messenger->routes;
>
>   /* The route to be replaced is somewhere down the list, or not there. */
>   while ( 1 ) {
>     /* No route in list had same pattern. */
>     if ( ! route->next ) {
>       route->next = new_route;
>       return 0;
>     }
>     /* Bingo ! */
>     if ( ! strcmp ( route->next->pattern, new_route->pattern ) ) {
>       old = route->next;
>       new_route->next = old->next;
>       route->next = new_route;
>       free ( (char *) old );
>       return 0;
>     }
>
>     route = route->next;
>   }
>
>   return 0;
> }
>

I'd have the following comments on the specific code. First I think if you
want to consider this extension you should also think about deleting
routes, e.g. maybe pass in a NULL address. Second, the set of routing rules
is matched in order. The way you've coded this the existing routing rule
will be removed and a new one will be added at the tail end. This means it
will have the lowest priority, and if you have a catch all rule configured,
e.g. "* -> default-gateway", then it will never match anything. It seems
like in such a scenario you would actually want to update the existing rule
in place rather than removing the old one. However if you're
programatically adding a brand new one, you still might have an issue if
you've previously added a more generic pattern. I'm thinking if you want to
support programatic manipulation of the routing table rather than treating
it as a simple configuration interface, we will need to add some explicit
notion of priority. I think these are all promising ideas to explore,
although I don't know how much we actually want to try to stuff into the
next release.

--Rafael

Reply via email to