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

io.camunda.zeebe.gateway.admin.exporting.ExportingControlService Maven / Gradle / Ivy

There is a newer version: 8.6.0-alpha5
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.gateway.admin.exporting;

import io.camunda.zeebe.broker.client.api.BrokerClient;
import io.camunda.zeebe.broker.client.api.BrokerClusterState;
import io.camunda.zeebe.gateway.admin.BrokerAdminRequest;
import io.camunda.zeebe.gateway.admin.IncompleteTopologyException;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.agrona.collections.IntHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExportingControlService implements ExportingControlApi {
  private static final Logger LOG = LoggerFactory.getLogger(ExportingControlService.class);
  final BrokerClient brokerClient;

  public ExportingControlService(final BrokerClient brokerClient) {
    this.brokerClient = brokerClient;
  }

  @Override
  public CompletableFuture pauseExporting() {
    LOG.info("Pausing exporting on all partitions.");
    final var topology = brokerClient.getTopologyManager().getTopology();
    return broadcastOnTopology(topology, BrokerAdminRequest::pauseExporting);
  }

  @Override
  public CompletableFuture softPauseExporting() {
    LOG.info("Soft Pausing exporting on all partitions.");
    final var topology = brokerClient.getTopologyManager().getTopology();
    return broadcastOnTopology(topology, BrokerAdminRequest::softPauseExporting);
  }

  @Override
  public CompletableFuture resumeExporting() {
    LOG.info("Resuming exporting on all partitions.");
    final var topology = brokerClient.getTopologyManager().getTopology();
    return broadcastOnTopology(topology, BrokerAdminRequest::resumeExporting);
  }

  private CompletableFuture broadcastOnTopology(
      final BrokerClusterState topology, final Consumer configureRequest) {
    validateTopology(topology);

    final var requests =
        topology.getPartitions().stream()
            .map(partition -> broadcastOnPartition(topology, partition, configureRequest))
            .toArray(CompletableFuture[]::new);
    return CompletableFuture.allOf(requests);
  }

  private CompletableFuture broadcastOnPartition(
      final BrokerClusterState topology,
      final Integer partitionId,
      final Consumer configureRequest) {

    final var leader = topology.getLeaderForPartition(partitionId);
    final var followers =
        Optional.ofNullable(topology.getFollowersForPartition(partitionId)).orElseGet(Set::of);
    final var inactive =
        Optional.ofNullable(topology.getInactiveNodesForPartition(partitionId)).orElseGet(Set::of);

    final var members = new IntHashSet(topology.getReplicationFactor());
    members.add(leader);
    members.addAll(followers);
    members.addAll(inactive);

    final var requests =
        members.stream()
            .map(
                brokerId -> {
                  final var request = new BrokerAdminRequest();
                  request.setBrokerId(brokerId);
                  request.setPartitionId(partitionId);
                  configureRequest.accept(request);
                  return brokerClient.sendRequest(request);
                })
            .toArray(CompletableFuture[]::new);
    return CompletableFuture.allOf(requests);
  }

  private void validateTopology(final BrokerClusterState topology) {
    final var replicationFactor = topology.getReplicationFactor();
    final var expectedPartitions = topology.getPartitionsCount();
    final var partitions = topology.getPartitions();

    if (partitions.size() != expectedPartitions) {
      throw new IncompleteTopologyException(
          "Found %s partitions but expected %s, current topology: %s"
              .formatted(partitions.size(), expectedPartitions, topology));
    }

    for (final var partition : partitions) {
      final var leaderId = topology.getLeaderForPartition(partition);

      if (leaderId == BrokerClusterState.UNKNOWN_NODE_ID
          || leaderId == BrokerClusterState.NODE_ID_NULL) {
        throw new IncompleteTopologyException(
            "Leader %s of partition %s is not known, current topology: %s"
                .formatted(leaderId, partition, topology));
      }

      final var followers =
          Optional.ofNullable(topology.getFollowersForPartition(partition))
              .orElse(Collections.emptySet());
      for (final var follower : followers) {
        if (follower == BrokerClusterState.UNKNOWN_NODE_ID
            || follower == BrokerClusterState.NODE_ID_NULL) {
          throw new IncompleteTopologyException(
              "Follower %s of partition %s is not known, current topology: %s"
                  .formatted(follower, partition, topology));
        }
      }

      final var memberCount = followers.size() + 1;
      if (memberCount != replicationFactor) {
        throw new IncompleteTopologyException(
            "Expected %s members of partition %s but found %s, current topology: %s"
                .formatted(replicationFactor, partition, memberCount, topology));
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy