io.camunda.zeebe.broker.system.partitions.impl.steps.InterPartitionCommandServiceStep Maven / Gradle / Ivy
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.camunda.zeebe.broker.system.partitions.impl.steps;
import io.atomix.raft.RaftServer.Role;
import io.camunda.zeebe.broker.system.partitions.PartitionTransitionContext;
import io.camunda.zeebe.broker.system.partitions.PartitionTransitionStep;
import io.camunda.zeebe.broker.transport.partitionapi.InterPartitionCommandReceiverActor;
import io.camunda.zeebe.broker.transport.partitionapi.InterPartitionCommandSenderService;
import io.camunda.zeebe.scheduler.future.ActorFuture;
public final class InterPartitionCommandServiceStep implements PartitionTransitionStep {
@Override
public ActorFuture prepareTransition(
final PartitionTransitionContext context, final long term, final Role targetRole) {
final ActorFuture closeFuture = context.getConcurrencyControl().createFuture();
final var receiverClosed = closeReceiver(context);
final var senderClosed = closeSender(context);
receiverClosed.onComplete(
(ignore, error) -> {
if (error != null) {
closeFuture.completeExceptionally(
"Failed to close InterPartitionCommandReceiver", error);
} else {
waitForSenderToClose(closeFuture, senderClosed);
}
});
return closeFuture;
}
@Override
public ActorFuture transitionTo(
final PartitionTransitionContext context, final long term, final Role targetRole) {
if (targetRole == Role.LEADER && context.getPartitionCommandReceiver() == null) {
final ActorFuture installFuture = context.getConcurrencyControl().createFuture();
final var receiverInstalled = installReceiver(context);
final var senderInstalled = installSender(context);
receiverInstalled.onComplete(
(ignore, error) -> {
if (error != null) {
installFuture.completeExceptionally(
"Failed to install InterPartitionCommandReceiver", error);
} else {
waitForSenderToStart(installFuture, senderInstalled);
}
});
return installFuture;
}
return context.getConcurrencyControl().createCompletedFuture();
}
@Override
public String getName() {
return "InterPartitionCommandService";
}
private void completeAfterFuture(
final ActorFuture futureToComplete,
final ActorFuture futureToWait,
final String errorMessage) {
futureToWait.onComplete(
(ok, senderError) -> {
if (senderError != null) {
futureToComplete.completeExceptionally(errorMessage, senderError);
} else {
futureToComplete.complete(null);
}
});
}
private void waitForSenderToClose(
final ActorFuture closeFuture, final ActorFuture senderClosed) {
completeAfterFuture(closeFuture, senderClosed, "Failed to close InterPartitionCommandSender");
}
private void waitForSenderToStart(
final ActorFuture startFuture, final ActorFuture senderInstalled) {
completeAfterFuture(
startFuture, senderInstalled, "Failed to install InterPartitionCommandSender");
}
private ActorFuture installReceiver(final PartitionTransitionContext context) {
final ActorFuture future = context.getConcurrencyControl().createFuture();
final var logStreamWriter = context.getLogStream().newLogStreamWriter();
final var receiver =
new InterPartitionCommandReceiverActor(
context.getPartitionId(), context.getClusterCommunicationService(), logStreamWriter);
context
.getActorSchedulingService()
.submitActor(receiver)
.onComplete(
(ignore, error) -> {
if (error != null) {
future.completeExceptionally(error);
} else {
context.setPartitionCommandReceiver(receiver);
context.getCheckpointProcessor().addCheckpointListener(receiver);
future.complete(null);
}
});
return future;
}
private ActorFuture installSender(final PartitionTransitionContext context) {
final ActorFuture future = context.getConcurrencyControl().createFuture();
final var sender =
new InterPartitionCommandSenderService(
context.getClusterCommunicationService(), context.getPartitionId());
final var actorStarted = context.getActorSchedulingService().submitActor(sender);
actorStarted.onComplete(
(ignore, error) -> {
if (error == null) {
context.setPartitionCommandSender(sender);
context.getTopologyManager().addTopologyPartitionListener(sender);
context.getCheckpointProcessor().addCheckpointListener(sender);
future.complete(null);
} else {
future.completeExceptionally(error);
}
});
return future;
}
private ActorFuture closeReceiver(final PartitionTransitionContext context) {
final var receiver = context.getPartitionCommandReceiver();
if (receiver != null) {
final ActorFuture receiverCloseFuture = receiver.closeAsync();
receiverCloseFuture.onComplete((res, error) -> context.setPartitionCommandReceiver(null));
return receiverCloseFuture;
} else {
return context.getConcurrencyControl().createCompletedFuture();
}
}
private ActorFuture closeSender(final PartitionTransitionContext context) {
final var sender = context.getPartitionCommandSender();
if (sender != null) {
context.getTopologyManager().removeTopologyPartitionListener(sender);
final ActorFuture receiverCloseFuture = sender.closeAsync();
receiverCloseFuture.onComplete((res, error) -> context.setPartitionCommandReceiver(null));
return receiverCloseFuture;
} else {
return context.getConcurrencyControl().createCompletedFuture();
}
}
}