You are viewing a plain text version of this content. The canonical link for it is here.
Posted to proton@qpid.apache.org by Michael Goulish <mg...@redhat.com> on 2013/04/08 19:45:01 UTC

possible cool change to pn_messenger_route()

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?


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;
}

Re: possible cool change to pn_messenger_route()

Posted by Alan Conway <ac...@redhat.com>.
On 04/09/2013 03:59 PM, Rafael Schloming wrote:
> On Tue, Apr 9, 2013 at 2:00 PM, Michael Goulish <mg...@redhat.com> wrote:
>
>>
>>
>> I like your  pattern --> { broadcast, balance, failover } ( addr, addr,
>> ... addr )
>> idea a lot better than my multiple rules w/ same pattern.
>>
>> I just think runtime-adaptive address translation could be very powerful.
>>
>> i.e. for failover scenario, think of (old) Qpid clustering -- when one node
>> failed, a new node with a new addr stands up -- and advertises its addr.
>> It would get added to this rule
>>
>>      pattern -->  failover ( addr1, addr2, addr_new )
>>
>> And the dead node deleted.
>>
>
> I agree it is a very tantalizing possibility. I think application-specific
> content based routing is the killer motivating scenario as that is
> something where you couldn't use a turn-key router, and also not something
> you are going to do via a config file.
>

Its my feeling that this functionality should be built on top of proton, not as 
part of proton - exactly because its difficult to cover all the things that 
people might want to do in a generic router. The routing library/plugin could 
provide the kind of routing table discussed on the list by default, but could 
also be presented as a toolkit that lets you plug your own routing code in, use 
your own config files or database etc.


Re: possible cool change to pn_messenger_route()

Posted by Rafael Schloming <rh...@alum.mit.edu>.
On Tue, Apr 9, 2013 at 2:00 PM, Michael Goulish <mg...@redhat.com> wrote:

>
>
> I like your  pattern --> { broadcast, balance, failover } ( addr, addr,
> ... addr )
> idea a lot better than my multiple rules w/ same pattern.
>
> I just think runtime-adaptive address translation could be very powerful.
>
> i.e. for failover scenario, think of (old) Qpid clustering -- when one node
> failed, a new node with a new addr stands up -- and advertises its addr.
> It would get added to this rule
>
>     pattern -->  failover ( addr1, addr2, addr_new )
>
> And the dead node deleted.
>

I agree it is a very tantalizing possibility. I think application-specific
content based routing is the killer motivating scenario as that is
something where you couldn't use a turn-key router, and also not something
you are going to do via a config file.

--Rafael

Re: possible cool change to pn_messenger_route()

Posted by Michael Goulish <mg...@redhat.com>.

I like your  pattern --> { broadcast, balance, failover } ( addr, addr, ... addr )
idea a lot better than my multiple rules w/ same pattern.

I just think runtime-adaptive address translation could be very powerful.

i.e. for failover scenario, think of (old) Qpid clustering -- when one node
failed, a new node with a new addr stands up -- and advertises its addr.
It would get added to this rule    

    pattern -->  failover ( addr1, addr2, addr_new )

And the dead node deleted.





----- Original Message -----
On Mon, Apr 8, 2013 at 1:45 PM, Michael Goulish <mg...@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

Re: possible cool change to pn_messenger_route()

Posted by Rafael Schloming <rh...@alum.mit.edu>.
On Mon, Apr 8, 2013 at 1:45 PM, Michael Goulish <mg...@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