@felipeboso I don’t think that it implementing an a/b strategy in Apigee would be very hard to do, in general. I think if you specifically want to use the Apigee Load Balancer in order to implement that strategy, it introduces some extra twists.
I think from your explanation you want to couple routing with payload modification. Something like this:
- use a weighted random approach to select either A or B as a target. Maybe A gets 6% of load and B gets 94%. Or, you could use some other criteria to select between A and B. Maybe you use a custom attribute attached to the client or API key in Apigee, and that attributes indicates whether A or B is selected. Or maybe it’s based on time-of-day. Or the presence of a header in the inbound request. Or some combination of these things. It doesn’t matter. The point is, you’ve got a way to select the target.
- If routing to A, then keep payload as-is. If routing to B, then modify the payload (request body) in a specific way.
- Apigee then invokes the selected target
What Dane is saying is … You can implement this in JavaScript pretty easily.
Within a JS step, you can set target.url variable to a different setting, corresponding to either A or B being selected. This JS step MUST be attached to the target request flow.
Also within a JS step, you can perform payload modification, or if you’d rather use XSL or similar to modify the payload, then just set a context variable in JS, and then your flow can refer to that context variable to conditionally execute the XSL (or similar).
Let’s look more specifically at how you might implement a weighted random selection.
To make this happen, you need the API proxy to be able to reference a 2-element array, containing the URL for A and B, and the weights for each of A and B. It might look like this:
[
{ "name": "a", "url": "https://a.example.com", "weight": 6 },
{ "name": "b", "url": "https://b.example.com", "weight": 94 }
]
It’s an array of objects. The inner elements contains a name, a url, and a weight. The Apigee KVM is a good place to store this kind of routing table, but you could also store that in some external system, that is accessible via ServiceCallout. If you use the latter you will want to wrap the callout with a cache. It’s not important that the weights add up to 100, of course. Your weighted random selector logic can easily normalize.
OK, then you have JS that looks something like this:
var routingTable = JSON.parse(context.getVariable('variable-containing-routing-table'));
var wrs = new WeightedRandomSelector(routingTable);
var selectedRoute = wrs.select(); // returns one of the items in the table
context.setVariable('target.url', selectedRoute.url);
// Option 1: modifying payload within this JS
if (selectedRoute.name == "a") {
var p = JSON.parse(context.getVariable('request.content'));
// example here only
p.additionalElement = "foo";
delete p.unwantedElement;
context.setVariable('request.content', JSON.stringify(p));
}
// Option 2: modifying payload externally (some other step)
context.setVariable('selected_route', selectedRoute.name);
In the Option 1 case, you just need the one JS step in the target request flow. Like this:
<TargetEndpoint name="target-1">
<Description/>
<FaultRules/>
<PreFlow name="PreFlow">
<Request>
<Step>
<Name>JS-SelectRoute</Name>
</Step>
</Request>
<Response/>
</PreFlow>
...
In the Option 2 case, you need the JS step, and the additional step wrapped in a Condition.
<TargetEndpoint name="target-1">
<Description/>
<FaultRules/>
<PreFlow name="PreFlow">
<Request>
<Step>
<Name>JS-SelectRoute</Name>
</Step>
<Step>
<Name>XSL-ModifyPayload</Name>
<Condition>selected_route = "a"</Condition>
</Step>
</Request>
<Response/>
</PreFlow>
...
That will work. The only remaining unsolved part is the weighted random selector. Find an example of that here. Of course your criteria for selection might be more complicated than that (time of day, custom attribute, etc). And … in that case, implementing that selector is up to you.