use of io.atomix.utils.concurrent.ThreadContext in project atomix by atomix.
the class RaftFuzzTest method runFuzzTest.
/**
* Runs a single fuzz test.
*/
private void runFuzzTest() throws Exception {
reset();
createServers(randomNumber(5) + 3);
final Object lock = new Object();
final AtomicLong index = new AtomicLong();
final Map<Integer, Long> indexes = new HashMap<>();
ThreadContext context = new SingleThreadContext("fuzz-test");
int clients = randomNumber(10) + 1;
for (int i = 0; i < clients; i++) {
ReadConsistency consistency = randomConsistency();
RaftClient client = createClient();
PrimitiveProxy proxy = createProxy(client, consistency);
Scheduler scheduler = new SingleThreadContext("fuzz-test-" + i);
final int clientId = i;
scheduler.schedule(Duration.ofMillis((100 * clients) + (randomNumber(50) - 25)), Duration.ofMillis((100 * clients) + (randomNumber(50) - 25)), () -> {
long lastLinearizableIndex = index.get();
int type = randomNumber(4);
switch(type) {
case 0:
proxy.<Map.Entry<String, String>, Long>invoke(PUT, clientSerializer::encode, Maps.immutableEntry(randomKey(), randomString(1024 * 16)), clientSerializer::decode).thenAccept(result -> {
synchronized (lock) {
if (result < lastLinearizableIndex) {
System.out.println(result + " is less than last linearizable index " + lastLinearizableIndex);
System.exit(1);
} else if (result > index.get()) {
index.set(result);
}
Long lastSequentialIndex = indexes.get(clientId);
if (lastSequentialIndex == null) {
indexes.put(clientId, result);
} else if (result < lastSequentialIndex) {
System.out.println(result + " is less than last sequential index " + lastSequentialIndex);
System.exit(1);
} else {
indexes.put(clientId, lastSequentialIndex);
}
}
});
break;
case 1:
proxy.invoke(GET, clientSerializer::encode, randomKey(), clientSerializer::decode);
break;
case 2:
proxy.<String, Long>invoke(REMOVE, clientSerializer::encode, randomKey(), clientSerializer::decode).thenAccept(result -> {
synchronized (lock) {
if (result < lastLinearizableIndex) {
System.out.println(result + " is less than last linearizable index " + lastLinearizableIndex);
System.exit(1);
} else if (result > index.get()) {
index.set(result);
}
Long lastSequentialIndex = indexes.get(clientId);
if (lastSequentialIndex == null) {
indexes.put(clientId, result);
} else if (result < lastSequentialIndex) {
System.out.println(result + " is less than last sequential index " + lastSequentialIndex);
System.exit(1);
} else {
indexes.put(clientId, lastSequentialIndex);
}
}
});
break;
case 3:
proxy.<Long>invoke(INDEX, clientSerializer::decode).thenAccept(result -> {
synchronized (lock) {
switch(consistency) {
case LINEARIZABLE:
case LINEARIZABLE_LEASE:
if (result < lastLinearizableIndex) {
System.out.println(result + " is less than last linearizable index " + lastLinearizableIndex);
System.exit(1);
} else if (result > index.get()) {
index.set(result);
}
case SEQUENTIAL:
Long lastSequentialIndex = indexes.get(clientId);
if (lastSequentialIndex == null) {
indexes.put(clientId, result);
} else if (result < lastSequentialIndex) {
System.out.println(result + " is less than last sequential index " + lastSequentialIndex);
System.exit(1);
} else {
indexes.put(clientId, lastSequentialIndex);
}
}
}
});
}
});
}
scheduleRestarts(context);
Thread.sleep(Duration.ofMinutes(15).toMillis());
}
use of io.atomix.utils.concurrent.ThreadContext in project atomix by atomix.
the class RaftProxyInvokerTest method testExpireSessionOnCommandFailure.
/**
* Tests that the client's session is expired when an UnknownSessionException is received from the cluster.
*/
@Test
public void testExpireSessionOnCommandFailure() throws Throwable {
CompletableFuture<CommandResponse> future = new CompletableFuture<>();
RaftProxyConnection connection = mock(RaftProxyConnection.class);
Mockito.when(connection.command(any(CommandRequest.class))).thenReturn(future);
RaftProxyState state = new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000);
RaftProxyManager manager = mock(RaftProxyManager.class);
ThreadContext threadContext = new TestContext();
RaftProxyInvoker submitter = new RaftProxyInvoker(connection, mock(RaftProxyConnection.class), state, new RaftProxySequencer(state), manager, threadContext);
CompletableFuture<byte[]> result = submitter.invoke(new PrimitiveOperation(COMMAND, HeapBytes.EMPTY));
assertEquals(state.getResponseIndex(), 1);
assertFalse(result.isDone());
future.completeExceptionally(new RaftException.UnknownSession("unknown session"));
assertTrue(result.isCompletedExceptionally());
}
use of io.atomix.utils.concurrent.ThreadContext in project atomix by atomix.
the class RaftProxyInvokerTest method testSkippingOverFailedQuery.
/**
* Tests skipping over a failed query attempt.
*/
@Test
public void testSkippingOverFailedQuery() throws Throwable {
CompletableFuture<QueryResponse> future1 = new CompletableFuture<>();
CompletableFuture<QueryResponse> future2 = new CompletableFuture<>();
RaftProxyConnection connection = mock(RaftProxyConnection.class);
Mockito.when(connection.query(any(QueryRequest.class))).thenReturn(future1).thenReturn(future2);
RaftProxyState state = new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000);
RaftProxyManager manager = mock(RaftProxyManager.class);
ThreadContext threadContext = new TestContext();
RaftProxyInvoker submitter = new RaftProxyInvoker(mock(RaftProxyConnection.class), connection, state, new RaftProxySequencer(state), manager, threadContext);
CompletableFuture<byte[]> result1 = submitter.invoke(new PrimitiveOperation(QUERY, HeapBytes.EMPTY));
CompletableFuture<byte[]> result2 = submitter.invoke(new PrimitiveOperation(QUERY, HeapBytes.EMPTY));
assertEquals(state.getResponseIndex(), 1);
assertFalse(result1.isDone());
assertFalse(result2.isDone());
future1.completeExceptionally(new RaftException.QueryFailure("failure"));
future2.complete(QueryResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(10).withResult("Hello world!".getBytes()).build());
assertTrue(result1.isCompletedExceptionally());
assertTrue(result2.isDone());
assertTrue(Arrays.equals(result2.get(), "Hello world!".getBytes()));
assertEquals(state.getResponseIndex(), 10);
}
use of io.atomix.utils.concurrent.ThreadContext in project atomix by atomix.
the class RaftProxyInvokerTest method testSubmitQuery.
/**
* Tests submitting a query to the cluster.
*/
@Test
public void testSubmitQuery() throws Throwable {
RaftProxyConnection connection = mock(RaftProxyConnection.class);
when(connection.query(any(QueryRequest.class))).thenReturn(CompletableFuture.completedFuture(QueryResponse.builder().withStatus(RaftResponse.Status.OK).withIndex(10).withResult("Hello world!".getBytes()).build()));
RaftProxyState state = new RaftProxyState("test", SessionId.from(1), UUID.randomUUID().toString(), new TestPrimitiveType(), 1000);
RaftProxyManager manager = mock(RaftProxyManager.class);
ThreadContext threadContext = new TestContext();
RaftProxyInvoker submitter = new RaftProxyInvoker(mock(RaftProxyConnection.class), connection, state, new RaftProxySequencer(state), manager, threadContext);
assertTrue(Arrays.equals(submitter.invoke(new PrimitiveOperation(QUERY, HeapBytes.EMPTY)).get(), "Hello world!".getBytes()));
assertEquals(state.getResponseIndex(), 10);
}
use of io.atomix.utils.concurrent.ThreadContext in project atomix by atomix.
the class RaftProxyManager method openSession.
/**
* Opens a new session.
*
* @param serviceName The session name.
* @param primitiveType The session type.
* @param communicationStrategy The strategy with which to communicate with servers.
* @param minTimeout The minimum session timeout.
* @param maxTimeout The maximum session timeout.
* @return A completable future to be completed once the session has been opened.
*/
public CompletableFuture<RaftProxyState> openSession(String serviceName, PrimitiveType primitiveType, ReadConsistency readConsistency, CommunicationStrategy communicationStrategy, Duration minTimeout, Duration maxTimeout) {
checkNotNull(serviceName, "serviceName cannot be null");
checkNotNull(primitiveType, "serviceType cannot be null");
checkNotNull(communicationStrategy, "communicationStrategy cannot be null");
checkNotNull(maxTimeout, "timeout cannot be null");
log.debug("Opening session; name: {}, type: {}", serviceName, primitiveType);
OpenSessionRequest request = OpenSessionRequest.builder().withNodeId(nodeId).withServiceName(serviceName).withServiceType(primitiveType).withReadConsistency(readConsistency).withMinTimeout(minTimeout.toMillis()).withMaxTimeout(maxTimeout.toMillis()).build();
CompletableFuture<RaftProxyState> future = new CompletableFuture<>();
ThreadContext proxyContext = threadContextFactory.createContext();
connection.openSession(request).whenCompleteAsync((response, error) -> {
if (error == null) {
if (response.status() == RaftResponse.Status.OK) {
// Create and store the proxy state.
RaftProxyState state = new RaftProxyState(clientId, SessionId.from(response.session()), serviceName, primitiveType, response.timeout());
sessions.put(state.getSessionId().id(), state);
state.addStateChangeListener(s -> {
if (s == PrimitiveProxy.State.CLOSED) {
sessions.remove(state.getSessionId().id());
}
});
// Ensure the proxy session info is reset and the session is kept alive.
keepAliveSessions(System.currentTimeMillis(), state.getSessionTimeout());
future.complete(state);
} else {
future.completeExceptionally(new RaftException.Unavailable(response.error().message()));
}
} else {
future.completeExceptionally(new RaftException.Unavailable(error.getMessage()));
}
}, proxyContext);
return future;
}
Aggregations