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

io.zeebe.gateway.impl.broker.cluster.BrokerTopologyManagerImpl 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 Zeebe Community License 1.0. You may not use this file
 * except in compliance with the Zeebe Community License 1.0.
 */
package io.zeebe.gateway.impl.broker.cluster;

import io.atomix.cluster.ClusterMembershipEvent;
import io.atomix.cluster.ClusterMembershipEvent.Type;
import io.atomix.cluster.ClusterMembershipEventListener;
import io.atomix.cluster.Member;
import io.zeebe.gateway.Loggers;
import io.zeebe.protocol.impl.encoding.BrokerInfo;
import io.zeebe.util.sched.Actor;
import java.time.Duration;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import org.slf4j.Logger;

public final class BrokerTopologyManagerImpl extends Actor
    implements BrokerTopologyManager, ClusterMembershipEventListener {

  private static final Logger LOG = Loggers.GATEWAY_LOGGER;

  protected final AtomicReference topology;
  private final Supplier> membersSupplier;

  public BrokerTopologyManagerImpl(final Supplier> membersSupplier) {
    this.membersSupplier = membersSupplier;
    topology = new AtomicReference<>(null);
  }

  /** @return the current known cluster state or null if the topology was not fetched yet */
  @Override
  public BrokerClusterState getTopology() {
    return topology.get();
  }

  public void setTopology(final BrokerClusterStateImpl topology) {
    this.topology.set(topology);
  }

  private void checkForMissingEvents() {
    final Set members = membersSupplier.get();
    if (members == null || members.isEmpty()) {
      return;
    }

    final BrokerClusterStateImpl newTopology = new BrokerClusterStateImpl(topology.get());
    for (final Member member : members) {
      final BrokerInfo brokerInfo = BrokerInfo.fromProperties(member.properties());
      if (brokerInfo != null) {
        newTopology.addBrokerIfAbsent(brokerInfo.getNodeId());
        processProperties(brokerInfo, newTopology);
      }
    }
    topology.set(newTopology);
  }

  @Override
  public String getName() {
    return "GatewayTopologyManager";
  }

  @Override
  protected void onActorStarted() {
    // to make gateway topology more robust we need to check for missing events periodically
    actor.runAtFixedRate(Duration.ofSeconds(5), this::checkForMissingEvents);
  }

  @Override
  public void event(final ClusterMembershipEvent event) {
    final Member subject = event.subject();
    final Type eventType = event.type();
    final BrokerInfo brokerInfo = BrokerInfo.fromProperties(subject.properties());

    if (brokerInfo != null) {
      actor.call(
          () -> {
            final BrokerClusterStateImpl newTopology = new BrokerClusterStateImpl(topology.get());

            switch (eventType) {
              case MEMBER_ADDED:
                LOG.debug("Received new broker {}.", brokerInfo);
                newTopology.addBrokerIfAbsent(brokerInfo.getNodeId());
                processProperties(brokerInfo, newTopology);
                break;

              case METADATA_CHANGED:
                LOG.debug(
                    "Received metadata change from Broker {}, partitions {}, terms {} and health {}.",
                    brokerInfo.getNodeId(),
                    brokerInfo.getPartitionRoles(),
                    brokerInfo.getPartitionLeaderTerms(),
                    brokerInfo.getPartitionHealthStatuses());
                newTopology.addBrokerIfAbsent(brokerInfo.getNodeId());
                processProperties(brokerInfo, newTopology);
                break;

              case MEMBER_REMOVED:
                LOG.debug("Received broker was removed {}.", brokerInfo);
                newTopology.removeBroker(brokerInfo.getNodeId());
                break;

              case REACHABILITY_CHANGED:
              default:
                LOG.debug(
                    "Received {} for broker {}, do nothing.", eventType, brokerInfo.getNodeId());
                break;
            }

            topology.set(newTopology);
          });
    }
  }

  // Update topology information based on the distributed event
  private void processProperties(
      final BrokerInfo distributedBrokerInfo, final BrokerClusterStateImpl newTopology) {

    newTopology.setClusterSize(distributedBrokerInfo.getClusterSize());
    newTopology.setPartitionsCount(distributedBrokerInfo.getPartitionsCount());
    newTopology.setReplicationFactor(distributedBrokerInfo.getReplicationFactor());

    final int nodeId = distributedBrokerInfo.getNodeId();

    distributedBrokerInfo.consumePartitions(
        newTopology::addPartitionIfAbsent,
        (leaderPartitionId, term) ->
            newTopology.setPartitionLeader(leaderPartitionId, nodeId, term),
        followerPartitionId -> newTopology.addPartitionFollower(followerPartitionId, nodeId));

    distributedBrokerInfo.consumePartitionsHealth(
        newTopology::addPartitionIfAbsent,
        partition -> newTopology.setPartitionHealthy(nodeId, partition),
        partition -> newTopology.setPartitionUnhealthy(nodeId, partition));

    final String clientAddress = distributedBrokerInfo.getCommandApiAddress();
    if (clientAddress != null) {
      newTopology.setBrokerAddressIfPresent(nodeId, clientAddress);
    }

    newTopology.setBrokerVersionIfPresent(nodeId, distributedBrokerInfo.getVersion());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy