@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, GSON.toJson(routeConfig.getRoutes())); }
private RouteConfig getConfig(ProgramId serviceId, Future<RouteConfig> future) { try { return future.get(ZK_TIMEOUT_SECS, TimeUnit.SECONDS); } catch (Exception e) { LOG.debug("Getting configuration for service {} from ZK failed.", serviceId, e); return new RouteConfig(Collections.<String, Integer>emptyMap()); } }
private int fetchWeight(@Nullable String version, RouteConfig routeConfig) { if (!routeConfig.isValid() || (version == null)) { // If route is not present, then allow some randomness by setting non-zero weight return 1; } Integer weight = routeConfig.getRoutes().get(version); if (weight == null) { // If this version is not present in the routeConfig, then don't choose that discoverable return 0; } return weight; } }
@Test public void testAbsenceOfConfig() throws Exception { ApplicationId appId = new ApplicationId("n1", "a1"); final ProgramId programId = appId.service("testAbsenceOfConfig"); try (final RouteStore routeStore = new ZKRouteStore(zkClientService)) { Assert.assertNotNull(routeStore.fetch(programId)); Assert.assertTrue(routeStore.fetch(programId).getRoutes().isEmpty()); routeStore.store(programId, new RouteConfig(TEST_ROUTE_CONFIG)); Tasks.waitFor(false, new Callable<Boolean>() { @Override public Boolean call() throws Exception { return routeStore.fetch(programId).getRoutes().isEmpty(); } }, 5, TimeUnit.SECONDS); Assert.assertEquals(TEST_ROUTE_CONFIG, routeStore.fetch(programId).getRoutes()); } }
RouteConfig routeConfig = new RouteConfig(routes); if (!routeConfig.isValid()) { throw new BadRequestException("Route Percentage needs to add up to 100.");
@Test public void testChangeConfig() throws Exception { ApplicationId appId = new ApplicationId("n1", "a1"); final ProgramId s1 = appId.service("testChangeConfig"); // Create two RouteStore, one for writing, one for reading try (RouteStore writingRouteStore = new ZKRouteStore(zkClientService)) { try (final RouteStore readingRouteStore = new ZKRouteStore(zkClientService)) { writingRouteStore.store(s1, new RouteConfig(TEST_ROUTE_CONFIG)); Assert.assertEquals(TEST_ROUTE_CONFIG, readingRouteStore.fetch(s1).getRoutes()); writingRouteStore.store(s1, new RouteConfig(TEST_ROUTE_CONFIG2)); Tasks.waitFor(TEST_ROUTE_CONFIG2, new Callable<Map<String, Integer>>() { @Override public Map<String, Integer> call() throws Exception { return readingRouteStore.fetch(s1).getRoutes(); } }, 5, TimeUnit.SECONDS, 100, TimeUnit.MILLISECONDS); } } } }
RouteConfig routeConfig = new RouteConfig(routes); if (!routeConfig.isValid()) { throw new BadRequestException("Route Percentage needs to add up to 100.");
@Test public void testCreateOfConfig() throws Exception { // This test first the config is missing and then created ApplicationId appId = new ApplicationId("n1", "a1"); final ProgramId programId = appId.service("testCreateOfConfig"); try (final RouteStore writeRouteStore = new ZKRouteStore(zkClientService)) { try (final RouteStore readRouteStore = new ZKRouteStore(zkClientService)) { // First it should be no route Assert.assertTrue(readRouteStore.fetch(programId).getRoutes().isEmpty()); // Write a new route config writeRouteStore.store(programId, new RouteConfig(TEST_ROUTE_CONFIG)); // Fetch it again and it should has the changes Tasks.waitFor(TEST_ROUTE_CONFIG, new Callable<Map<String, Integer>>() { @Override public Map<String, Integer> call() throws Exception { return readRouteStore.fetch(programId).getRoutes(); } }, 5L, TimeUnit.SECONDS, 100, TimeUnit.MILLISECONDS); } } }
@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, GSON.toJson(routeConfig.getRoutes())); }
private RouteConfig getConfig(ProgramId serviceId, Future<RouteConfig> future) { try { return future.get(ZK_TIMEOUT_SECS, TimeUnit.SECONDS); } catch (Exception e) { LOG.debug("Getting configuration for service {} from ZK failed.", serviceId, e); return new RouteConfig(Collections.<String, Integer>emptyMap()); } }
@Test public void testStore() throws Exception { ApplicationId appId = new ApplicationId("n1", "a1"); final ProgramId s1 = appId.service("testStore"); try (final RouteStore routeStore = new ZKRouteStore(zkClientService)) { routeStore.store(s1, new RouteConfig(TEST_ROUTE_CONFIG)); Assert.assertEquals(TEST_ROUTE_CONFIG, routeStore.fetch(s1).getRoutes()); routeStore.delete(s1); Tasks.waitFor(true, new Callable<Boolean>() { @Override public Boolean call() throws Exception { return routeStore.fetch(s1).getRoutes().isEmpty(); } }, 5L, TimeUnit.SECONDS, 100, TimeUnit.MILLISECONDS); } }
@Override public void store(final ProgramId serviceId, final RouteConfig routeConfig) { Transactionals.execute(transactional, context -> { getRouteTable(context).write(ServiceDiscoverable.getName(serviceId), GSON.toJson(routeConfig.getRoutes())); }); }
@Override public void onFailure(Throwable t) { // If no node is present, then set an empty RouteConfig (instead of setting exception since generating // stack trace, when future.get is called, is expensive). Also place a watch on the node to monitor further // data updates. If its an exception other than NoNodeException, then remove the future, so that // zkClient#getData will be retried during the next fetch config request if (t instanceof KeeperException.NoNodeException) { settableFuture.set(new RouteConfig(Collections.<String, Integer>emptyMap())); existsAndWatch(serviceId, settableFuture); } else { settableFuture.setException(t); routeConfigMap.remove(serviceId, settableFuture); } } });
@Test public void testRouteStorage() throws Exception { RouteStore routeStore = new LocalRouteStore(datasetFramework, new InMemoryTxSystemClient(txManager)); ApplicationId appId = new ApplicationId("n1", "a1"); ProgramId service1 = appId.service("s1"); RouteConfig routeConfig = new RouteConfig(ImmutableMap.of("v1", 100)); routeStore.store(service1, routeConfig); Assert.assertEquals(routeConfig.getRoutes(), routeStore.fetch(service1).getRoutes()); routeStore.delete(service1); Assert.assertNotNull(routeStore.fetch(service1)); try { routeStore.delete(service1); Assert.fail("Config should have been deleted and thus a NotFoundException must have been thrown."); } catch (NotFoundException e) { // expected } } }
@Override public void store(final ProgramId serviceId, final RouteConfig routeConfig) { Transactionals.execute(transactional, context -> { getRouteTable(context).write(ServiceDiscoverable.getName(serviceId), GSON.toJson(routeConfig.getRoutes())); }); }
@Override public void onFailure(Throwable t) { // If no node is present, then set an empty RouteConfig (instead of setting exception since generating // stack trace, when future.get is called, is expensive). Also place a watch on the node to monitor further // data updates. If its an exception other than NoNodeException, then remove the future, so that // zkClient#getData will be retried during the next fetch config request if (t instanceof KeeperException.NoNodeException) { settableFuture.set(new RouteConfig(Collections.<String, Integer>emptyMap())); existsAndWatch(serviceId, settableFuture); } else { settableFuture.setException(t); routeConfigMap.remove(serviceId, settableFuture); } } });
@Override public Boolean call() throws Exception { return routeStore.fetch(programId).getRoutes().isEmpty(); } }, 5, TimeUnit.SECONDS);
@Override public RouteConfig fetch(final ProgramId serviceId) { return Transactionals.execute(transactional, context -> { byte[] value = getRouteTable(context).read(ServiceDiscoverable.getName(serviceId)); if (value == null) { return new RouteConfig(Collections.emptyMap()); } return new RouteConfig(GSON.fromJson(Bytes.toString(value), MAP_STRING_INTEGER_TYPE)); }); }
@Override public Boolean call() throws Exception { return routeStore.fetch(s1).getRoutes().isEmpty(); } }, 5L, TimeUnit.SECONDS, 100, TimeUnit.MILLISECONDS);