All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.zeebe.broker.system.partitions.impl.steps.InterPartitionCommandServiceStep Maven / Gradle / Ivy

There is a newer version: 8.7.0-alpha1
Show newest version
/*
 * 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();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy