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

io.camunda.zeebe.broker.transport.adminapi.AdminApiRequestHandler 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.transport.adminapi;

import io.atomix.raft.RaftServer.Role;
import io.atomix.raft.partition.RaftPartition;
import io.camunda.zeebe.broker.partitioning.PartitionAdminAccess;
import io.camunda.zeebe.broker.system.configuration.FlowControlCfg;
import io.camunda.zeebe.broker.transport.AsyncApiRequestHandler;
import io.camunda.zeebe.broker.transport.ErrorResponseWriter;
import io.camunda.zeebe.logstreams.impl.flowcontrol.LimitSerializer;
import io.camunda.zeebe.protocol.management.AdminRequestType;
import io.camunda.zeebe.scheduler.future.ActorFuture;
import io.camunda.zeebe.scheduler.future.CompletableActorFuture;
import io.camunda.zeebe.transport.RequestType;
import io.camunda.zeebe.transport.impl.AtomixServerTransport;
import io.camunda.zeebe.util.Either;
import java.io.IOException;

public class AdminApiRequestHandler
    extends AsyncApiRequestHandler {
  private final AtomixServerTransport transport;
  private final PartitionAdminAccess adminAccess;
  private final RaftPartition raftPartition;

  public AdminApiRequestHandler(
      final AtomixServerTransport transport,
      final PartitionAdminAccess adminAccess,
      final RaftPartition raftPartition) {
    super(ApiRequestReader::new, ApiResponseWriter::new);
    this.transport = transport;
    this.adminAccess = adminAccess;

    this.raftPartition = raftPartition;
  }

  @Override
  protected void onActorStarting() {
    transport.subscribe(raftPartition.id().id(), RequestType.ADMIN, this);
  }

  @Override
  protected void onActorClosing() {
    transport.unsubscribe(raftPartition.id().id(), RequestType.ADMIN);
  }

  @Override
  protected ActorFuture> handleAsync(
      final int partitionId,
      final long requestId,
      final ApiRequestReader requestReader,
      final ApiResponseWriter responseWriter,
      final ErrorResponseWriter errorWriter) {
    return switch (requestReader.getMessageDecoder().type()) {
      case STEP_DOWN_IF_NOT_PRIMARY ->
          CompletableActorFuture.completed(
              stepDownIfNotPrimary(responseWriter, partitionId, errorWriter));
      case PAUSE_EXPORTING -> pauseExporting(responseWriter, partitionId, errorWriter);
      case SOFT_PAUSE_EXPORTING -> softPauseExporting(responseWriter, partitionId, errorWriter);
      case RESUME_EXPORTING -> resumeExporting(responseWriter, partitionId, errorWriter);
      case BAN_INSTANCE -> banInstance(requestReader, responseWriter, partitionId, errorWriter);
      case GET_FLOW_CONTROL -> getFlowControl(responseWriter, errorWriter);
      case SET_FLOW_CONTROL -> setFlowControl(requestReader, responseWriter, errorWriter);
      default -> unknownRequest(errorWriter, requestReader.getMessageDecoder().type());
    };
  }

  private ActorFuture> setFlowControl(
      final ApiRequestReader requestReader,
      final ApiResponseWriter responseWriter,
      final ErrorResponseWriter errorWriter) {
    final ActorFuture> result = actor.createFuture();

    final String payload = requestReader.payload();
    final FlowControlCfg flowControlCfg;

    try {
      flowControlCfg = FlowControlCfg.deserialize(payload);
    } catch (final IOException e) {
      LOG.error("Failed to parse the flow control configuration: ", e);
      result.complete(
          Either.left(
              errorWriter.internalError(
                  "Failed to parse the flow control configuration: %s".formatted(e.getMessage()))));
      return result;
    }

    adminAccess
        .configureFlowControl(flowControlCfg)
        .onComplete(
            (r, t) -> {
              if (t == null) {
                result.complete(Either.right(responseWriter));
              } else {
                LOG.error("Failed to set the flow control configuration.", t);
                result.complete(
                    Either.left(
                        errorWriter.internalError(
                            "Failed to set the flow control configuration: %s"
                                .formatted(t.getMessage()))));
              }
            });
    return result;
  }

  private ActorFuture> getFlowControl(
      final ApiResponseWriter responseWriter, final ErrorResponseWriter errorWriter) {
    final ActorFuture> result = actor.createFuture();
    adminAccess
        .getFlowControlConfiguration()
        .onComplete(
            (r, t) -> {
              if (t == null) {
                responseWriter.setPayload(LimitSerializer.serialize(r));
                result.complete(Either.right(responseWriter));

              } else {
                LOG.error("Failed to get the flow control configuration.", t);
                result.complete(
                    Either.left(
                        errorWriter.internalError(
                            "Failed to get the flow control configuration.")));
              }
            });

    return result;
  }

  private ActorFuture> banInstance(
      final ApiRequestReader requestReader,
      final ApiResponseWriter responseWriter,
      final int partitionId,
      final ErrorResponseWriter errorWriter) {
    final long key = requestReader.key();

    final ActorFuture> result = actor.createFuture();
    adminAccess
        .banInstance(requestReader.key())
        .onComplete(
            (r, t) -> {
              if (t == null) {
                result.complete(Either.right(responseWriter));
              } else {
                LOG.error("Failed to ban instance {} on partition {}", key, partitionId, t);
                result.complete(
                    Either.left(
                        errorWriter.internalError(
                            "Failed to ban instance %s, on partition %s", key, partitionId)));
              }
            });

    return result;
  }

  private ActorFuture> unknownRequest(
      final ErrorResponseWriter errorWriter, final AdminRequestType type) {
    errorWriter.unsupportedMessage(type, AdminRequestType.values());
    return CompletableActorFuture.completed(Either.left(errorWriter));
  }

  private ActorFuture> pauseExporting(
      final ApiResponseWriter responseWriter,
      final int partitionId,
      final ErrorResponseWriter errorWriter) {
    final var partitionAdminAccess = adminAccess.forPartition(partitionId);
    if (partitionAdminAccess.isEmpty()) {
      return CompletableActorFuture.completed(
          Either.left(
              errorWriter.internalError(
                  "Partition %s failed to pause exporting. Could not find the partition.",
                  partitionId)));
    }

    final ActorFuture> result = actor.createFuture();
    partitionAdminAccess
        .orElseThrow()
        .pauseExporting()
        .onComplete(
            (r, t) -> {
              if (t == null) {
                result.complete(Either.right(responseWriter));
              } else {
                LOG.error("Failed to pause exporting on partition {}", partitionId, t);
                result.complete(
                    Either.left(
                        errorWriter.internalError(
                            "Partition %s failed to pause exporting", partitionId)));
              }
            });

    return result;
  }

  private ActorFuture> softPauseExporting(
      final ApiResponseWriter responseWriter,
      final int partitionId,
      final ErrorResponseWriter errorWriter) {
    final var partitionAdminAccess = adminAccess.forPartition(partitionId);
    if (partitionAdminAccess.isEmpty()) {
      return CompletableActorFuture.completed(
          Either.left(
              errorWriter.internalError(
                  "Partition %s failed to soft pause exporting. Could not find the partition.",
                  partitionId)));
    }

    final ActorFuture> result = actor.createFuture();
    partitionAdminAccess
        .orElseThrow()
        .softPauseExporting()
        .onComplete(
            (r, t) -> {
              if (t == null) {
                result.complete(Either.right(responseWriter));
              } else {
                LOG.error("Failed to soft pause exporting on partition {}", partitionId, t);
                result.complete(
                    Either.left(
                        errorWriter.internalError(
                            "Partition %s failed to soft pause exporting", partitionId)));
              }
            });

    return result;
  }

  private ActorFuture> resumeExporting(
      final ApiResponseWriter responseWriter,
      final int partitionId,
      final ErrorResponseWriter errorWriter) {
    final var partitionAdminAccess = adminAccess.forPartition(partitionId);
    if (partitionAdminAccess.isEmpty()) {
      return CompletableActorFuture.completed(
          Either.left(
              errorWriter.internalError(
                  "Partition %s failed to resume exporting. Could not find the partition.",
                  partitionId)));
    }

    final ActorFuture> result = actor.createFuture();
    partitionAdminAccess
        .orElseThrow()
        .resumeExporting()
        .onComplete(
            (r, t) -> {
              if (t == null) {
                result.complete(Either.right(responseWriter));
              } else {
                LOG.error("Failed to resume exporting on partition {}", partitionId, t);
                result.complete(
                    Either.left(
                        errorWriter.internalError(
                            "Partition %s failed to resume exporting", partitionId)));
              }
            });

    return result;
  }

  private Either stepDownIfNotPrimary(
      final ApiResponseWriter responseWriter,
      final int partitionId,
      final ErrorResponseWriter errorWriter) {
    if (raftPartition.getRole() == Role.LEADER) {
      raftPartition.stepDownIfNotPrimary();
    } else {
      errorWriter.partitionLeaderMismatch(partitionId);
      return Either.left(errorWriter);
    }

    return Either.right(responseWriter);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy