use of co.cask.cdap.route.store.RouteConfig in project cdap by caskdata.
the class UserServiceEndpointStrategyTest method testStrategy.
@Test
public void testStrategy() throws Exception {
ProgramId serviceId = new ApplicationId("n1", "a1").service("s1");
String discoverableName = ServiceDiscoverable.getName(serviceId);
List<Discoverable> candidates = new ArrayList<>();
for (int i = 0; i < 5; i++) {
candidates.add(new Discoverable(discoverableName, null, Bytes.toBytes(Integer.toString(i))));
}
SimpleServiceDiscovered serviceDiscovered = new SimpleServiceDiscovered(candidates);
Map<String, Integer> routeToVersion = ImmutableMap.of("2", 100);
Map<ProgramId, RouteConfig> routeMap = ImmutableMap.of(serviceId, new RouteConfig(routeToVersion));
RouteStore configStore = new InMemoryRouteStore(routeMap);
UserServiceEndpointStrategy strategy = new UserServiceEndpointStrategy(serviceDiscovered, configStore, serviceId);
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("2", Bytes.toString(picked.getPayload()));
}
// Switch config to choose version 3 always
routeToVersion = ImmutableMap.of("3", 100);
configStore.store(serviceId, new RouteConfig(routeToVersion));
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("3", Bytes.toString(picked.getPayload()));
}
// Switch config to choose verion 1 and 4 - 50% each
routeToVersion = ImmutableMap.of("1", 50, "4", 50);
configStore.store(serviceId, new RouteConfig(routeToVersion));
Map<String, Integer> resultMap = new HashMap<>();
for (int i = 0; i < 10000; i++) {
Discoverable picked = strategy.pick();
String version = Bytes.toString(picked.getPayload());
if (resultMap.containsKey(version)) {
resultMap.put(version, resultMap.get(version) + 1);
} else {
resultMap.put(version, 1);
}
}
Assert.assertEquals(2, resultMap.size());
double requestsToOne = resultMap.get("1");
double requestsToTwo = resultMap.get("4");
double requestRatio = requestsToOne / requestsToTwo;
// Request Ratio should be close to 1.0 since we expect 50% of requests to go to each of these versions
Assert.assertTrue(String.format("RequestRatio was %f and 1 got %f and 4 got %f", requestRatio, requestsToOne, requestsToTwo), requestRatio >= 0.7);
Assert.assertTrue(String.format("RequestRatio was %f and 1 got %f and 4 got %f", requestRatio, requestsToOne, requestsToTwo), requestRatio <= 1.3);
// Set the payload filter
strategy = new UserServiceEndpointStrategy(serviceDiscovered, configStore, serviceId, null, "1");
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("1", Bytes.toString(picked.getPayload()));
}
}
use of co.cask.cdap.route.store.RouteConfig in project cdap by caskdata.
the class RouteConfigHttpHandler method storeRouteConfig.
@PUT
@Path("/routeconfig")
@AuditPolicy(AuditDetail.REQUEST_BODY)
public void storeRouteConfig(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespaceId, @PathParam("app-id") String appId, @PathParam("service-id") String serviceId) throws Exception {
NamespaceId namespace = new NamespaceId(namespaceId);
ProgramId programId = namespace.app(appId).service(serviceId);
Map<String, Integer> routes = parseBody(request, ROUTE_CONFIG_TYPE);
if (routes == null || routes.isEmpty()) {
throw new BadRequestException("Route config contains invalid format or empty content.");
}
List<ProgramId> nonExistingServices = new ArrayList<>();
for (String version : routes.keySet()) {
ProgramId routeProgram = namespace.app(appId, version).service(serviceId);
if (lifecycleService.getProgramSpecification(routeProgram) == null) {
nonExistingServices.add(routeProgram);
}
}
if (nonExistingServices.size() > 0) {
throw new BadRequestException("The following versions of the application/service could not be found : " + nonExistingServices);
}
RouteConfig routeConfig = new RouteConfig(routes);
if (!routeConfig.isValid()) {
throw new BadRequestException("Route Percentage needs to add up to 100.");
}
routeStore.store(programId, routeConfig);
responder.sendStatus(HttpResponseStatus.OK);
}
use of co.cask.cdap.route.store.RouteConfig in project cdap by caskdata.
the class RouteConfigHttpHandler method getRouteConfig.
@GET
@Path("/routeconfig")
public void getRouteConfig(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespaceId, @PathParam("app-id") String appId, @PathParam("service-id") String serviceId) throws Exception {
ProgramId programId = Ids.namespace(namespaceId).app(appId).service(serviceId);
RouteConfig routeConfig = routeStore.fetch(programId);
responder.sendJson(HttpResponseStatus.OK, routeConfig.getRoutes());
}
use of co.cask.cdap.route.store.RouteConfig in project cdap by caskdata.
the class UserServiceEndpointStrategy method pick.
@Nullable
@Override
public Discoverable pick() {
if (version != null) {
return versionedRandomEndpointStrategy.pick();
}
RouteConfig routeConfig = routeStore.fetch(serviceId);
if (!routeConfig.isValid()) {
if (fallbackStrategy.equals(RouteFallbackStrategy.DROP)) {
return null;
}
if (fallbackStrategy.equals(RouteFallbackStrategy.RANDOM)) {
// Since the fallback strategy is RANDOM, we will do pull random pick across all discoverables
return versionedRandomEndpointStrategy.pick();
}
}
String minMaxVersion = null;
Iterator<Discoverable> iterator = serviceDiscovered.iterator();
Discoverable result = null;
int wSum = 0;
while (iterator.hasNext()) {
Discoverable candidate = iterator.next();
String version = Bytes.toString(candidate.getPayload());
int sign = 1;
if (!routeConfig.isValid()) {
// Fallback strategy is set to smallest/largest, so choose a random endpoint that has smallest/largest version
if (minMaxVersion == null) {
minMaxVersion = version;
} else {
int resetMinMaxVersion = minMaxVersion.compareTo(version);
resetMinMaxVersion *= fallbackStrategy.equals(RouteFallbackStrategy.SMALLEST) ? 1 : -1;
// consider it to be picked.
if (resetMinMaxVersion > 0) {
minMaxVersion = version;
// Since we found a version smaller/larger than the previously seen minMaxVersion, then reset the pick
result = null;
// Make randomPick to be negative to ensure this version gets picked
sign = -1;
} else if (resetMinMaxVersion < 0) {
continue;
}
}
}
int weight = fetchWeight(version, routeConfig);
wSum += weight;
// randomWeight is a random number in the range of [1, wSum]
int randomWeight = wSum > 0 ? ThreadLocalRandom.current().nextInt(1, wSum + 1) * sign : 1;
// pick the current candidate with probability weight/wSum
if (randomWeight <= weight) {
result = candidate;
}
}
return result;
}
use of co.cask.cdap.route.store.RouteConfig in project cdap by caskdata.
the class UserServiceEndpointStrategyTest method testFallback.
@Test
public void testFallback() throws Exception {
ProgramId serviceId = new ApplicationId("n1", "a1").service("s1");
String discoverableName = ServiceDiscoverable.getName(serviceId);
List<Discoverable> candidates = new ArrayList<>();
for (int i = 0; i < 5; i++) {
candidates.add(new Discoverable(discoverableName, null, Bytes.toBytes(Integer.toString(i))));
}
SimpleServiceDiscovered serviceDiscovered = new SimpleServiceDiscovered(candidates);
Map<ProgramId, RouteConfig> routeConfigMap = new HashMap<>();
routeConfigMap.put(serviceId, new RouteConfig(Collections.<String, Integer>emptyMap()));
RouteStore configStore = new InMemoryRouteStore(routeConfigMap);
UserServiceEndpointStrategy strategy = new UserServiceEndpointStrategy(serviceDiscovered, configStore, serviceId, RouteFallbackStrategy.SMALLEST, null);
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("0", Bytes.toString(picked.getPayload()));
}
// Remove "0", so the smallest version should now be "1"
candidates.remove(0);
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("1", Bytes.toString(picked.getPayload()));
}
// Test greatest strategy
strategy = new UserServiceEndpointStrategy(serviceDiscovered, configStore, serviceId, RouteFallbackStrategy.LARGEST, null);
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("4", Bytes.toString(picked.getPayload()));
}
// Remove "4", so the largest version should now be "3"
candidates.remove(candidates.size() - 1);
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
Assert.assertEquals("3", Bytes.toString(picked.getPayload()));
}
// Test random strategy - remaining versions are 1, 2, 3
strategy = new UserServiceEndpointStrategy(serviceDiscovered, configStore, serviceId, RouteFallbackStrategy.RANDOM, null);
Set<String> pickedVersions = new HashSet<>();
for (int i = 0; i < 1000; i++) {
Discoverable picked = strategy.pick();
pickedVersions.add(Bytes.toString(picked.getPayload()));
}
// There is a good probability that more than one version has been picked since its random
Assert.assertTrue(pickedVersions.size() > 1);
// Test drop strategy
strategy = new UserServiceEndpointStrategy(serviceDiscovered, configStore, serviceId, RouteFallbackStrategy.DROP, null);
for (int i = 0; i < 1000; i++) {
Assert.assertNull(strategy.pick());
}
}
Aggregations