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