com.sleepycat.je.rep.elections.Acceptor Maven / Gradle / Ivy
The newest version!
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.rep.elections;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.channels.Channels;
import java.util.logging.Level;
import com.sleepycat.je.EnvironmentFailureException;
import com.sleepycat.je.JEVersion;
import com.sleepycat.je.rep.elections.Acceptor.SuggestionGenerator.Ranking;
import com.sleepycat.je.rep.elections.Proposer.Proposal;
import com.sleepycat.je.rep.elections.Protocol.Accept;
import com.sleepycat.je.rep.elections.Protocol.Propose;
import com.sleepycat.je.rep.elections.Protocol.Value;
import com.sleepycat.je.rep.impl.TextProtocol.InvalidMessageException;
import com.sleepycat.je.rep.impl.TextProtocol.RequestMessage;
import com.sleepycat.je.rep.impl.TextProtocol.ResponseMessage;
import com.sleepycat.je.rep.impl.node.MasterTransfer;
import com.sleepycat.je.rep.impl.node.NameIdPair;
import com.sleepycat.je.rep.net.DataChannel;
import com.sleepycat.je.rep.utilint.ServiceDispatcher;
import com.sleepycat.je.utilint.LoggerUtils;
/**
* Plays the role of Acceptor in the consensus algorithm. It runs in its
* own thread listening for and responding to messages sent by Proposers.
*/
public class Acceptor extends ElectionAgentThread {
/*
* The currently promised proposal. Proposals below this one are rejected.
*/
private Proposal promisedProposal = null;
private Value acceptedValue = null;
/* Used to return suggestions in response to Propose requests. */
private final SuggestionGenerator suggestionGenerator;
/* Identifies the Acceptor Service. */
public static final String SERVICE_NAME = "Acceptor";
private final ElectionsConfig config;
/**
* Creates an Acceptor
*/
public Acceptor(Protocol protocol,
ElectionsConfig config,
SuggestionGenerator suggestionGenerator) {
super(config.getRepImpl(), protocol,
"Acceptor Thread " + config.getNameIdPair().getName());
this.config = config;
this.suggestionGenerator = suggestionGenerator;
}
/**
* The Acceptor thread body.
*/
@Override
public void run() {
final ServiceDispatcher serviceDispatcher =
config.getServiceDispatcher();
serviceDispatcher.register(SERVICE_NAME, channelQueue);
LoggerUtils.logMsg
(logger, envImpl, formatter, Level.FINE, "Acceptor started");
DataChannel channel = null;
try {
while (true) {
channel = serviceDispatcher.takeChannel
(SERVICE_NAME, protocol.getReadTimeout());
if (channel == null) {
/* A soft shutdown. */
return;
}
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(
new InputStreamReader(
Channels.newInputStream(channel)));
out = new PrintWriter(
Channels.newOutputStream(channel), true);
final String requestLine = in.readLine();
if (requestLine == null) {
LoggerUtils.logMsg(logger, envImpl,
formatter, Level.FINE,
"Acceptor: EOF on request");
continue;
}
RequestMessage requestMessage = null;
try {
requestMessage = protocol.parseRequest(requestLine);
} catch (InvalidMessageException ime) {
protocol.processIME(channel, ime);
continue;
}
if (requestMessage.getOp() == protocol.SHUTDOWN) {
break;
}
final MasterTransfer masterTransfer =
(config.getRepNode() != null) ?
config.getRepNode().getActiveTransfer() : null;
if (masterTransfer != null) {
final String msg =
"Acceptor ignoring request due to active master " +
"transfer initiated at:" + formatter.
getDate(masterTransfer.getStartTime());
LoggerUtils.logMsg(logger, envImpl,
formatter, Level.INFO, msg);
continue;
}
ResponseMessage responseMessage = null;
if (requestMessage.getOp() == protocol.PROPOSE) {
responseMessage = process((Propose) requestMessage);
} else if (requestMessage.getOp() == protocol.ACCEPT) {
responseMessage = process((Accept) requestMessage);
} else {
LoggerUtils.logMsg(logger, envImpl,
formatter, Level.SEVERE,
"Acceptor unrecognized request: " +
requestLine);
continue;
}
/*
* The request message may be of an earlier version. If so,
* this node transparently read the older version. JE only
* throws out InvalidMesageException when the version of
* the request message is newer than the current protocol.
* To avoid sending a repsonse that the requester cannot
* understand, we send a response in the same version as
* that of the original request message.
*/
responseMessage.setSendVersion
(requestMessage.getSendVersion());
out.println(responseMessage.wireFormat());
} catch (IOException e) {
LoggerUtils.logMsg
(logger, envImpl, formatter, Level.WARNING,
"Acceptor IO error on socket: " + e.getMessage());
continue;
} finally {
Utils.cleanup(logger, envImpl, formatter, channel, in, out);
cleanup();
}
}
} catch (InterruptedException e) {
if (isShutdown()) {
/* Treat it like a shutdown, exit the thread. */
return;
}
LoggerUtils.logMsg(logger, envImpl, formatter, Level.WARNING,
"Acceptor unexpected interrupted");
throw EnvironmentFailureException.unexpectedException(e);
} finally {
serviceDispatcher.cancel(SERVICE_NAME);
cleanup();
}
}
/**
* Responds to a Propose request.
*
* @param propose the request proposal
*
* @return the response: a Promise if the request was accepted, a Reject
* otherwise.
*/
ResponseMessage process(Propose propose) {
if ((promisedProposal != null) &&
(promisedProposal.compareTo(propose.getProposal()) > 0)) {
LoggerUtils.logMsg(logger, envImpl, formatter, Level.FINE,
"Reject Propose: " + propose.getProposal() +
" Promised proposal: " + promisedProposal);
return protocol.new Reject(promisedProposal);
}
promisedProposal = propose.getProposal();
final Value suggestedValue = suggestionGenerator.get(promisedProposal);
final Ranking suggestionRanking =
suggestionGenerator.getRanking(promisedProposal);
LoggerUtils.logMsg(logger, envImpl, formatter, Level.FINE,
"Promised: " + promisedProposal +
" Suggested Value: " + suggestedValue +
" Suggestion Ranking: " + suggestionRanking);
return protocol.new Promise(promisedProposal,
acceptedValue,
suggestedValue,
suggestionRanking,
config.getElectionPriority(),
config.getLogVersion(),
JEVersion.CURRENT_VERSION);
}
/**
* Responds to Accept request
*
* @param accept the request
* @return an Accepted or Reject response as appropriate.
*/
ResponseMessage process(Accept accept) {
if ((promisedProposal != null) &&
(promisedProposal.compareTo(accept.getProposal()) != 0)) {
LoggerUtils.logMsg(logger, envImpl, formatter, Level.FINE,
"Reject Accept: " + accept.getProposal() +
" Promised proposal: " + promisedProposal);
return protocol.new Reject(promisedProposal);
}
acceptedValue = accept.getValue();
LoggerUtils.logMsg(logger, envImpl, formatter, Level.FINE,
"Promised: " + promisedProposal + " Accepted: " +
accept.getProposal() + " Value: " + acceptedValue);
return protocol.new Accepted(accept.getProposal(), acceptedValue);
}
public interface SuggestionGenerator {
/**
* Used to generate a suggested value for use by a Proposer. It's a
* hint. The proposal argument may be used to freeze values like the
* VLSN number from advancing (if they were used in the ranking) until
* an election has completed.
*
* @param proposal the Proposal for which the value is being suggested.
*
* @return the suggested value.
*/
abstract Value get(Proposal proposal);
/**
* The importance associated with the above suggestion. Acceptors have
* to agree on a common system for ranking importance so that the
* relative importance of different suggestions can be meaningfully
* compared.
*
* @param proposal the proposal associated with the ranking
*
* @return the importance of the suggestion as a number
*/
abstract Ranking getRanking(Proposal proposal);
/**
* A description of the ranking used when comparing Promises to pick a
* Master.
*/
class Ranking implements Comparable {
/* The major component of the ranking. */
final long major;
/* The minor component. */
final long minor;
final long id;
static Ranking UNINITIALIZED =
new Ranking(Long.MIN_VALUE,
Long.MIN_VALUE,
NameIdPair.NULL_NODE_ID);
public Ranking(long major, long minor, long id) {
this.major = major;
this.minor = minor;
this.id = id;
}
public Ranking(long major, long minor) {
this.major = major;
this.minor = minor;
this.id = NameIdPair.NULL_NODE_ID;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (major ^ (major >>> 32));
result = prime * result + (int) (minor ^ (minor >>> 32));
result = prime * result + (int) (id);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Ranking other = (Ranking) obj;
if (major != other.major) {
return false;
}
if (minor != other.minor) {
return false;
}
if (id != other.id) {
return false;
}
return true;
}
@Override
public String toString() {
return
"Ranking major:" + major + " minor:" + minor + "id:" + id;
}
@Override
public int compareTo(Ranking o) {
int result = Long.compare(major, o.major);
if (result != 0) {
return result;
}
return Long.compare(minor, o.minor);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy