use of org.apache.flink.queryablestate.KvStateID in project flink by apache.
the class ClientTest method testServerClosesChannel.
/**
* Tests that a server channel close, closes the connection and removes it from the established
* connections.
*/
@Test
public void testServerClosesChannel() throws Exception {
AtomicKvStateRequestStats stats = new AtomicKvStateRequestStats();
final MessageSerializer<KvStateInternalRequest, KvStateResponse> serializer = new MessageSerializer<>(new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer());
Client<KvStateInternalRequest, KvStateResponse> client = null;
Channel serverChannel = null;
try {
client = new Client<>("Test Client", 1, serializer, stats);
final LinkedBlockingQueue<ByteBuf> received = new LinkedBlockingQueue<>();
final AtomicReference<Channel> channel = new AtomicReference<>();
serverChannel = createServerChannel(new ChannelDataCollectingHandler(channel, received));
InetSocketAddress serverAddress = getKvStateServerAddress(serverChannel);
// Requests
KvStateInternalRequest request = new KvStateInternalRequest(new KvStateID(), new byte[0]);
Future<KvStateResponse> future = client.sendRequest(serverAddress, request);
received.take();
assertEquals(1, stats.getNumConnections());
channel.get().close().await();
try {
future.get();
fail("Did not throw expected server failure");
} catch (ExecutionException e) {
if (!(e.getCause() instanceof ClosedChannelException)) {
fail("Did not throw expected Exception");
}
// Expected
}
assertEquals(0L, stats.getNumConnections());
// Counts can take some time to propagate
while (stats.getNumSuccessful() != 0L || stats.getNumFailed() != 1L) {
Thread.sleep(100L);
}
assertEquals(1L, stats.getNumRequests());
assertEquals(0L, stats.getNumSuccessful());
assertEquals(1L, stats.getNumFailed());
} finally {
if (client != null) {
try {
client.shutdown().get();
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(client.isEventGroupShutdown());
}
if (serverChannel != null) {
serverChannel.close();
}
assertEquals("Channel leak", 0L, stats.getNumConnections());
}
}
use of org.apache.flink.queryablestate.KvStateID in project flink by apache.
the class ClientTest method testConcurrentQueries.
/**
* Multiple threads concurrently fire queries.
*/
@Test
public void testConcurrentQueries() throws Exception {
AtomicKvStateRequestStats stats = new AtomicKvStateRequestStats();
final MessageSerializer<KvStateInternalRequest, KvStateResponse> serializer = new MessageSerializer<>(new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer());
ExecutorService executor = null;
Client<KvStateInternalRequest, KvStateResponse> client = null;
Channel serverChannel = null;
final byte[] serializedResult = new byte[1024];
ThreadLocalRandom.current().nextBytes(serializedResult);
try {
int numQueryTasks = 4;
final int numQueriesPerTask = 1024;
executor = Executors.newFixedThreadPool(numQueryTasks);
client = new Client<>("Test Client", 1, serializer, stats);
serverChannel = createServerChannel(new RespondingChannelHandler(serializer, serializedResult));
final InetSocketAddress serverAddress = getKvStateServerAddress(serverChannel);
final Client<KvStateInternalRequest, KvStateResponse> finalClient = client;
Callable<List<CompletableFuture<KvStateResponse>>> queryTask = () -> {
List<CompletableFuture<KvStateResponse>> results = new ArrayList<>(numQueriesPerTask);
for (int i = 0; i < numQueriesPerTask; i++) {
KvStateInternalRequest request = new KvStateInternalRequest(new KvStateID(), new byte[0]);
results.add(finalClient.sendRequest(serverAddress, request));
}
return results;
};
// Submit query tasks
List<Future<List<CompletableFuture<KvStateResponse>>>> futures = new ArrayList<>();
for (int i = 0; i < numQueryTasks; i++) {
futures.add(executor.submit(queryTask));
}
// Verify results
for (Future<List<CompletableFuture<KvStateResponse>>> future : futures) {
List<CompletableFuture<KvStateResponse>> results = future.get();
for (CompletableFuture<KvStateResponse> result : results) {
KvStateResponse actual = result.get();
assertArrayEquals(serializedResult, actual.getContent());
}
}
int totalQueries = numQueryTasks * numQueriesPerTask;
// Counts can take some time to propagate
while (stats.getNumSuccessful() != totalQueries) {
Thread.sleep(100L);
}
assertEquals(totalQueries, stats.getNumRequests());
assertEquals(totalQueries, stats.getNumSuccessful());
} finally {
if (executor != null) {
executor.shutdown();
}
if (serverChannel != null) {
serverChannel.close();
}
if (client != null) {
try {
client.shutdown().get();
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(client.isEventGroupShutdown());
}
assertEquals("Channel leak", 0L, stats.getNumConnections());
}
}
use of org.apache.flink.queryablestate.KvStateID in project flink by apache.
the class ClientTest method testFailureClosesChannel.
/**
* Tests that a server failure closes the connection and removes it from the established
* connections.
*/
@Test
public void testFailureClosesChannel() throws Exception {
AtomicKvStateRequestStats stats = new AtomicKvStateRequestStats();
final MessageSerializer<KvStateInternalRequest, KvStateResponse> serializer = new MessageSerializer<>(new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer());
Client<KvStateInternalRequest, KvStateResponse> client = null;
Channel serverChannel = null;
try {
client = new Client<>("Test Client", 1, serializer, stats);
final LinkedBlockingQueue<ByteBuf> received = new LinkedBlockingQueue<>();
final AtomicReference<Channel> channel = new AtomicReference<>();
serverChannel = createServerChannel(new ChannelDataCollectingHandler(channel, received));
InetSocketAddress serverAddress = getKvStateServerAddress(serverChannel);
// Requests
List<Future<KvStateResponse>> futures = new ArrayList<>();
KvStateInternalRequest request = new KvStateInternalRequest(new KvStateID(), new byte[0]);
futures.add(client.sendRequest(serverAddress, request));
futures.add(client.sendRequest(serverAddress, request));
ByteBuf buf = received.take();
assertNotNull("Receive timed out", buf);
buf.release();
buf = received.take();
assertNotNull("Receive timed out", buf);
buf.release();
assertEquals(1L, stats.getNumConnections());
Channel ch = channel.get();
assertNotNull("Channel not active", ch);
// Respond with failure
ch.writeAndFlush(MessageSerializer.serializeServerFailure(serverChannel.alloc(), new RuntimeException("Expected test server failure")));
try {
futures.remove(0).get();
fail("Did not throw expected server failure");
} catch (ExecutionException e) {
if (!(e.getCause() instanceof RuntimeException)) {
fail("Did not throw expected Exception");
}
// Expected
}
try {
futures.remove(0).get();
fail("Did not throw expected server failure");
} catch (ExecutionException e) {
if (!(e.getCause() instanceof RuntimeException)) {
fail("Did not throw expected Exception");
}
// Expected
}
assertEquals(0L, stats.getNumConnections());
// Counts can take some time to propagate
while (stats.getNumSuccessful() != 0L || stats.getNumFailed() != 2L) {
Thread.sleep(100L);
}
assertEquals(2L, stats.getNumRequests());
assertEquals(0L, stats.getNumSuccessful());
assertEquals(2L, stats.getNumFailed());
} finally {
if (client != null) {
try {
client.shutdown().get();
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(client.isEventGroupShutdown());
}
if (serverChannel != null) {
serverChannel.close();
}
assertEquals("Channel leak", 0L, stats.getNumConnections());
}
}
use of org.apache.flink.queryablestate.KvStateID in project flink by apache.
the class ClientTest method testRequestUnavailableHost.
/**
* Tests that a request to an unavailable host is failed with ConnectException.
*/
@Test
public void testRequestUnavailableHost() throws Exception {
AtomicKvStateRequestStats stats = new AtomicKvStateRequestStats();
MessageSerializer<KvStateInternalRequest, KvStateResponse> serializer = new MessageSerializer<>(new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer());
Client<KvStateInternalRequest, KvStateResponse> client = null;
try (NetUtils.Port port = NetUtils.getAvailablePort()) {
client = new Client<>("Test Client", 1, serializer, stats);
int availablePort = port.getPort();
InetSocketAddress serverAddress = new InetSocketAddress(InetAddress.getLocalHost(), availablePort);
KvStateInternalRequest request = new KvStateInternalRequest(new KvStateID(), new byte[0]);
CompletableFuture<KvStateResponse> future = client.sendRequest(serverAddress, request);
assertThat(future, FlinkMatchers.futureWillCompleteExceptionally(ConnectException.class, Duration.ofHours(1)));
} finally {
if (client != null) {
try {
client.shutdown().get();
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(client.isEventGroupShutdown());
}
assertEquals("Channel leak", 0L, stats.getNumConnections());
}
}
use of org.apache.flink.queryablestate.KvStateID in project flink by apache.
the class ClientTest method testClientServerIntegration.
/**
* Tests multiple clients querying multiple servers until 100k queries have been processed. At
* this point, the client is shut down and its verified that all ongoing requests are failed.
*/
@Test
public void testClientServerIntegration() throws Throwable {
// Config
final int numServers = 2;
final int numServerEventLoopThreads = 2;
final int numServerQueryThreads = 2;
final int numClientEventLoopThreads = 4;
final int numClientsTasks = 8;
final int batchSize = 16;
final int numKeyGroups = 1;
AbstractStateBackend abstractBackend = new MemoryStateBackend();
KvStateRegistry dummyRegistry = new KvStateRegistry();
DummyEnvironment dummyEnv = new DummyEnvironment("test", 1, 0);
dummyEnv.setKvStateRegistry(dummyRegistry);
AbstractKeyedStateBackend<Integer> backend = abstractBackend.createKeyedStateBackend(dummyEnv, new JobID(), "test_op", IntSerializer.INSTANCE, numKeyGroups, new KeyGroupRange(0, 0), dummyRegistry.createTaskRegistry(new JobID(), new JobVertexID()), TtlTimeProvider.DEFAULT, new UnregisteredMetricsGroup(), Collections.emptyList(), new CloseableRegistry());
AtomicKvStateRequestStats clientStats = new AtomicKvStateRequestStats();
final MessageSerializer<KvStateInternalRequest, KvStateResponse> serializer = new MessageSerializer<>(new KvStateInternalRequest.KvStateInternalRequestDeserializer(), new KvStateResponse.KvStateResponseDeserializer());
Client<KvStateInternalRequest, KvStateResponse> client = null;
ExecutorService clientTaskExecutor = null;
final KvStateServerImpl[] server = new KvStateServerImpl[numServers];
try {
client = new Client<>("Test Client", numClientEventLoopThreads, serializer, clientStats);
clientTaskExecutor = Executors.newFixedThreadPool(numClientsTasks);
// Create state
ValueStateDescriptor<Integer> desc = new ValueStateDescriptor<>("any", IntSerializer.INSTANCE);
desc.setQueryable("any");
// Create servers
KvStateRegistry[] registry = new KvStateRegistry[numServers];
AtomicKvStateRequestStats[] serverStats = new AtomicKvStateRequestStats[numServers];
final KvStateID[] ids = new KvStateID[numServers];
for (int i = 0; i < numServers; i++) {
registry[i] = new KvStateRegistry();
serverStats[i] = new AtomicKvStateRequestStats();
server[i] = new KvStateServerImpl(InetAddress.getLocalHost().getHostName(), Collections.singletonList(0).iterator(), numServerEventLoopThreads, numServerQueryThreads, registry[i], serverStats[i]);
server[i].start();
backend.setCurrentKey(1010 + i);
// Value per server
ValueState<Integer> state = backend.getPartitionedState(VoidNamespace.INSTANCE, VoidNamespaceSerializer.INSTANCE, desc);
state.update(201 + i);
// we know it must be a KvState but this is not exposed to the user via State
InternalKvState<Integer, ?, Integer> kvState = (InternalKvState<Integer, ?, Integer>) state;
// Register KvState (one state instance for all server)
ids[i] = registry[i].registerKvState(new JobID(), new JobVertexID(), new KeyGroupRange(0, 0), "any", kvState, getClass().getClassLoader());
}
final Client<KvStateInternalRequest, KvStateResponse> finalClient = client;
Callable<Void> queryTask = () -> {
while (true) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
// Random server permutation
List<Integer> random = new ArrayList<>();
for (int j = 0; j < batchSize; j++) {
random.add(j);
}
Collections.shuffle(random);
// Dispatch queries
List<CompletableFuture<KvStateResponse>> futures = new ArrayList<>(batchSize);
for (int j = 0; j < batchSize; j++) {
int targetServer = random.get(j) % numServers;
byte[] serializedKeyAndNamespace = KvStateSerializer.serializeKeyAndNamespace(1010 + targetServer, IntSerializer.INSTANCE, VoidNamespace.INSTANCE, VoidNamespaceSerializer.INSTANCE);
KvStateInternalRequest request = new KvStateInternalRequest(ids[targetServer], serializedKeyAndNamespace);
futures.add(finalClient.sendRequest(server[targetServer].getServerAddress(), request));
}
// Verify results
for (int j = 0; j < batchSize; j++) {
int targetServer = random.get(j) % numServers;
Future<KvStateResponse> future = futures.get(j);
byte[] buf = future.get().getContent();
int value = KvStateSerializer.deserializeValue(buf, IntSerializer.INSTANCE);
assertEquals(201L + targetServer, value);
}
}
};
// Submit tasks
List<Future<Void>> taskFutures = new ArrayList<>();
for (int i = 0; i < numClientsTasks; i++) {
taskFutures.add(clientTaskExecutor.submit(queryTask));
}
long numRequests;
while ((numRequests = clientStats.getNumRequests()) < 100_000L) {
Thread.sleep(100L);
LOG.info("Number of requests {}/100_000", numRequests);
}
try {
client.shutdown().get();
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(client.isEventGroupShutdown());
final CombinableMatcher<Throwable> exceptionMatcher = either(FlinkMatchers.containsCause(ClosedChannelException.class)).or(FlinkMatchers.containsCause(IllegalStateException.class));
for (Future<Void> future : taskFutures) {
try {
future.get();
fail("Did not throw expected Exception after shut down");
} catch (ExecutionException t) {
assertThat(t, exceptionMatcher);
}
}
assertEquals("Connection leak (client)", 0L, clientStats.getNumConnections());
for (int i = 0; i < numServers; i++) {
boolean success = false;
int numRetries = 0;
while (!success) {
try {
assertEquals("Connection leak (server)", 0L, serverStats[i].getNumConnections());
success = true;
} catch (Throwable t) {
if (numRetries < 10) {
LOG.info("Retrying connection leak check (server)");
Thread.sleep((numRetries + 1) * 50L);
numRetries++;
} else {
throw t;
}
}
}
}
} finally {
if (client != null) {
try {
client.shutdown().get();
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(client.isEventGroupShutdown());
}
for (int i = 0; i < numServers; i++) {
if (server[i] != null) {
server[i].shutdown();
}
}
if (clientTaskExecutor != null) {
clientTaskExecutor.shutdown();
}
}
}
Aggregations