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

io.atomix.raft.partition.RaftPartition Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-present Open Networking Foundation
 * Copyright © 2020 camunda services GmbH ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.atomix.raft.partition;

import static com.google.common.base.MoreObjects.toStringHelper;

import io.atomix.cluster.MemberId;
import io.atomix.primitive.partition.Partition;
import io.atomix.primitive.partition.PartitionId;
import io.atomix.primitive.partition.PartitionManagementService;
import io.atomix.primitive.partition.PartitionMetadata;
import io.atomix.raft.RaftRoleChangeListener;
import io.atomix.raft.RaftServer.Role;
import io.atomix.raft.cluster.RaftMember;
import io.atomix.raft.partition.impl.RaftPartitionServer;
import io.camunda.zeebe.snapshots.ReceivableSnapshotStore;
import io.camunda.zeebe.util.VisibleForTesting;
import io.camunda.zeebe.util.health.FailureListener;
import io.camunda.zeebe.util.health.HealthMonitorable;
import io.camunda.zeebe.util.health.HealthReport;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Abstract partition. */
public final class RaftPartition implements Partition, HealthMonitorable {
  private static final Logger LOG = LoggerFactory.getLogger(RaftPartition.class);
  private static final String PARTITION_NAME_FORMAT = "%s-partition-%d";
  private final PartitionId partitionId;
  private final RaftPartitionConfig config;
  private final File dataDirectory;
  private final Set deferredRoleChangeListeners =
      new CopyOnWriteArraySet<>();
  private final PartitionMetadata partitionMetadata;
  private RaftPartitionServer server;

  public RaftPartition(
      final PartitionMetadata partitionMetadata,
      final RaftPartitionConfig config,
      final File dataDirectory) {
    partitionId = partitionMetadata.id();
    this.partitionMetadata = partitionMetadata;
    this.config = config;
    this.dataDirectory = dataDirectory;
  }

  public void addRoleChangeListener(final RaftRoleChangeListener listener) {
    if (server == null) {
      deferredRoleChangeListeners.add(listener);
    } else {
      server.addRoleChangeListener(listener);
    }
  }

  public void removeRoleChangeListener(final RaftRoleChangeListener listener) {
    deferredRoleChangeListeners.remove(listener);
    server.removeRoleChangeListener(listener);
  }

  /**
   * Returns the partition data directory.
   *
   * @return the partition data directory
   */
  public File dataDirectory() {
    return dataDirectory;
  }

  /** Bootstraps a partition. */
  public CompletableFuture bootstrap(
      final PartitionManagementService managementService,
      final ReceivableSnapshotStore snapshotStore) {
    if (partitionMetadata
        .members()
        .contains(managementService.getMembershipService().getLocalMember().id())) {
      initServer(managementService, snapshotStore);
      return server.bootstrap().thenApply(v -> this);
    }
    return CompletableFuture.completedFuture(this);
  }

  public CompletableFuture join(
      final PartitionManagementService managementService,
      final ReceivableSnapshotStore snapshotStore) {
    initServer(managementService, snapshotStore);
    return server.join().thenApply(v -> this);
  }

  public CompletableFuture leave() {
    return server.leave().thenApply(v -> this);
  }

  private void initServer(
      final PartitionManagementService managementService,
      final ReceivableSnapshotStore snapshotStore) {
    server = createServer(managementService, snapshotStore);

    if (!deferredRoleChangeListeners.isEmpty()) {
      deferredRoleChangeListeners.forEach(server::addRoleChangeListener);
      deferredRoleChangeListeners.clear();
    }
  }

  /** Creates a Raft server. */
  private RaftPartitionServer createServer(
      final PartitionManagementService managementService,
      final ReceivableSnapshotStore snapshotStore) {
    return new RaftPartitionServer(
        this,
        config,
        managementService.getMembershipService().getLocalMember().id(),
        managementService.getMembershipService(),
        managementService.getMessagingService(),
        snapshotStore,
        partitionMetadata);
  }

  /**
   * Returns the partition name.
   *
   * @return the partition name
   */
  public String name() {
    return String.format(PARTITION_NAME_FORMAT, partitionId.group(), partitionId.id());
  }

  @Override
  public String getName() {
    return name();
  }

  @Override
  public HealthReport getHealthReport() {
    return server.getHealthReport();
  }

  @Override
  public void addFailureListener(final FailureListener failureListener) {
    server.addFailureListener(failureListener);
  }

  @Override
  public void removeFailureListener(final FailureListener failureListener) {
    server.removeFailureListener(failureListener);
  }

  /** Closes the partition. */
  public CompletableFuture close() {
    return closeServer()
        .exceptionally(
            error -> {
              LOG.error("Error on shutdown partition: {}.", partitionId, error);
              return null;
            });
  }

  private CompletableFuture closeServer() {
    if (server != null) {
      return server.stop();
    }
    return CompletableFuture.completedFuture(null);
  }

  /**
   * Deletes the partition.
   *
   * @return future to be completed once the partition has been deleted
   */
  public CompletableFuture delete() {
    return server
        .stop()
        .thenRun(
            () -> {
              if (server != null) {
                server.delete();
              }
            });
  }

  @Override
  public String toString() {
    return toStringHelper(this).add("partitionId", id()).toString();
  }

  @Override
  public PartitionId id() {
    return partitionId;
  }

  @Override
  public long term() {
    return server != null ? server.getTerm() : 0;
  }

  @Override
  public Collection members() {
    final var membersFromServer = server != null ? server.getMembers() : null;
    if (membersFromServer != null) {
      // Use members from server if available. This will reflect changes when members leave or join.
      return membersFromServer.stream().map(RaftMember::memberId).collect(Collectors.toSet());
    } else {
      // Fall back to static partition metadata so that we can still get the members of a partition
      // that hasn't been started yet. This is necessary for bootstrap.
      return partitionMetadata != null ? partitionMetadata.members() : Collections.emptyList();
    }
  }

  public Role getRole() {
    return server != null ? server.getRole() : null;
  }

  public RaftPartitionServer getServer() {
    return server;
  }

  public CompletableFuture stepDown() {
    return server.stepDown();
  }

  /**
   * Tries to step down if the following conditions are met:
   *
   * 
    *
  • priority election is enabled *
  • the partition distributor determined a primary node *
  • this node is not the primary *
*/ public CompletableFuture stepDownIfNotPrimary() { if (shouldStepDown()) { LOG.info( "Decided that {} should step down as {} from partition {} because {} is primary", server.getMemberId(), server.getRole(), partitionMetadata.id(), partitionMetadata.getPrimary().orElse(null)); return stepDown(); } else { return CompletableFuture.completedFuture(null); } } @VisibleForTesting public boolean shouldStepDown() { final var primary = partitionMetadata.getPrimary(); return server != null && config.isPriorityElectionEnabled() && primary.isPresent() && !primary.get().equals(server.getMemberId()); } public CompletableFuture stop() { return server.stop(); } public RaftPartitionConfig getPartitionConfig() { return config; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy