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

io.atomix.raft.roles.AbstractRole Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015-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.roles;

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

import io.atomix.cluster.MemberId;
import io.atomix.raft.RaftError;
import io.atomix.raft.RaftError.Type;
import io.atomix.raft.RaftException;
import io.atomix.raft.RaftServer;
import io.atomix.raft.cluster.impl.DefaultRaftMember;
import io.atomix.raft.impl.RaftContext;
import io.atomix.raft.protocol.RaftRequest;
import io.atomix.raft.protocol.RaftResponse;
import io.atomix.utils.logging.ContextualLoggerFactory;
import io.atomix.utils.logging.LoggerContext;
import io.camunda.zeebe.util.Either;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import org.slf4j.Logger;

/** Abstract state. */
public abstract class AbstractRole implements RaftRole {

  protected final Logger log;
  protected final RaftContext raft;
  private volatile boolean open = true;

  protected AbstractRole(final RaftContext raft) {
    this.raft = raft;
    log =
        ContextualLoggerFactory.getLogger(
            getClass(),
            LoggerContext.builder(RaftServer.class)
                .addValue(raft.getName())
                .add("role", role())
                .build());
  }

  @Override
  public Either shouldAcceptRequest(final RaftRequest request) {
    // Reject only if we are in force configuration change and the request is not from the members
    // of forced configuration
    final boolean shouldReject =
        raft.getCluster().getConfiguration() != null
            && raft.getCluster().getConfiguration().force()
            && !raft.getCluster().getConfiguration().hasMember(request.from());
    if (shouldReject) {
      return Either.left(
          new RaftError(
              Type.ILLEGAL_MEMBER_STATE,
              String.format(
                  "Force configuration change is in progress. Cannot accept request from %s which is not a member of the new configuration.",
                  request.from())));
    } else {
      return Either.right(null);
    }
  }

  /** Logs a request. */
  protected final void logRequest(final Object request) {
    log.trace("Received {}", request);
  }

  /** Logs a response. */
  protected final  R logResponse(final R response) {
    log.trace("Sending {}", response);
    return response;
  }

  @Override
  public CompletableFuture start() {
    raft.checkThread();
    open = true;
    return CompletableFuture.completedFuture(null);
  }

  @Override
  public boolean isRunning() {
    return open;
  }

  @Override
  public CompletableFuture stop() {
    raft.checkThread();
    open = false;
    return CompletableFuture.completedFuture(null);
  }

  /** Forwards the given request to the leader if possible. */
  protected  CompletableFuture forward(
      final T request, final BiFunction> function) {
    final CompletableFuture future = new CompletableFuture<>();
    final DefaultRaftMember leader = raft.getLeader();
    if (leader == null) {
      return CompletableFuture.failedFuture(new RaftException.NoLeader("No leader found"));
    }

    function
        .apply(leader.memberId(), request)
        .whenCompleteAsync(
            (response, error) -> {
              if (error == null) {
                future.complete(response);
              } else {
                future.completeExceptionally(error);
              }
            },
            raft.getThreadContext());
    return future;
  }

  /** Updates the term and leader. */
  protected boolean updateTermAndLeader(final long term, final MemberId leader) {
    // If the request indicates a term that is greater than the current term or no leader has been
    // set for the current term, update leader and term.
    if (term > raft.getTerm()
        || (term == raft.getTerm() && raft.getLeader() == null && leader != null)) {
      raft.setTerm(term);
      raft.setLeader(leader);

      // Reset the current cluster configuration to the last committed configuration when a leader
      // change occurs.
      raft.getCluster().reset();
      return true;
    }
    return false;
  }

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