Search in sources :

Example 1 with RouteConfig

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()));
    }
}
Also used : ServiceDiscoverable(co.cask.cdap.common.service.ServiceDiscoverable) Discoverable(org.apache.twill.discovery.Discoverable) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) RouteStore(co.cask.cdap.route.store.RouteStore) RouteConfig(co.cask.cdap.route.store.RouteConfig) ProgramId(co.cask.cdap.proto.id.ProgramId) ApplicationId(co.cask.cdap.proto.id.ApplicationId) Test(org.junit.Test)

Example 2 with RouteConfig

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);
}
Also used : ArrayList(java.util.ArrayList) BadRequestException(co.cask.cdap.common.BadRequestException) RouteConfig(co.cask.cdap.route.store.RouteConfig) NamespaceId(co.cask.cdap.proto.id.NamespaceId) ProgramId(co.cask.cdap.proto.id.ProgramId) Path(javax.ws.rs.Path) AuditPolicy(co.cask.cdap.common.security.AuditPolicy) PUT(javax.ws.rs.PUT)

Example 3 with RouteConfig

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());
}
Also used : RouteConfig(co.cask.cdap.route.store.RouteConfig) ProgramId(co.cask.cdap.proto.id.ProgramId) Path(javax.ws.rs.Path) GET(javax.ws.rs.GET)

Example 4 with RouteConfig

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;
}
Also used : Discoverable(org.apache.twill.discovery.Discoverable) RouteConfig(co.cask.cdap.route.store.RouteConfig) Nullable(javax.annotation.Nullable)

Example 5 with RouteConfig

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());
    }
}
Also used : ServiceDiscoverable(co.cask.cdap.common.service.ServiceDiscoverable) Discoverable(org.apache.twill.discovery.Discoverable) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) RouteStore(co.cask.cdap.route.store.RouteStore) RouteConfig(co.cask.cdap.route.store.RouteConfig) ProgramId(co.cask.cdap.proto.id.ProgramId) ApplicationId(co.cask.cdap.proto.id.ApplicationId) HashSet(java.util.HashSet) Test(org.junit.Test)

Aggregations

RouteConfig (co.cask.cdap.route.store.RouteConfig)5 ProgramId (co.cask.cdap.proto.id.ProgramId)4 ArrayList (java.util.ArrayList)3 Discoverable (org.apache.twill.discovery.Discoverable)3 ServiceDiscoverable (co.cask.cdap.common.service.ServiceDiscoverable)2 ApplicationId (co.cask.cdap.proto.id.ApplicationId)2 RouteStore (co.cask.cdap.route.store.RouteStore)2 HashMap (java.util.HashMap)2 Path (javax.ws.rs.Path)2 Test (org.junit.Test)2 BadRequestException (co.cask.cdap.common.BadRequestException)1 AuditPolicy (co.cask.cdap.common.security.AuditPolicy)1 NamespaceId (co.cask.cdap.proto.id.NamespaceId)1 HashSet (java.util.HashSet)1 Nullable (javax.annotation.Nullable)1 GET (javax.ws.rs.GET)1 PUT (javax.ws.rs.PUT)1