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

io.permazen.kv.raft.msg.AppendRequest Maven / Gradle / Ivy


/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.kv.raft.msg;

import com.google.common.base.Preconditions;

import io.permazen.kv.raft.Timestamp;
import io.permazen.util.LongEncoder;

import java.nio.ByteBuffer;

/**
 * Sent from leaders to followers to probe the follower's log state and/or append an entry to their log.
 *
 * 

* Instances also provide the {@linkplain #getLeaderLeaseTimeout leader's lease timeout} value (if any), * which is used to commit read-only transactions, as well as a {@linkplain #getLeaderTimestamp leader timestamp} * which should be reflected back in the corresponding {@link AppendResponse}. */ public class AppendRequest extends Message { private final Timestamp leaderTimestamp; // leader's timestamp for this request private final Timestamp leaderLeaseTimeout; // earliest leader timestamp at which time leader could be deposed private final long leaderCommit; // index of highest log entry known to be committed private final long prevLogTerm; // term of previous log entry private final long prevLogIndex; // index of previous log entry private final long logEntryTerm; // term corresponding to log entry, or zero if this is a "probe" private ByteBuffer mutationData; // serialized mutations, if not a probe and not from follower transaction private boolean mutationDataInvalid; // mutationData has already been grabbed // Constructors /** * Constructor for a "probe" that does not contain a log entry. * *

* Note that {@code leaderCommit} is limited to {@code prevLogIndex} by this constructor, because we can't guarantee * that the omitted log entry at {@code prevLogIndex + 1} matches whatever the follower has in its log. * * @param clusterId cluster ID * @param senderId identity of sender * @param recipientId identity of recipient * @param term sender's current term * @param leaderTimestamp leader's timestamp for this request * @param leaderLeaseTimeout earliest leader timestamp at which leader could be deposed (or null) * @param leaderCommit current commit index for sender * @param prevLogTerm term of the log entry just prior to this one * @param prevLogIndex index of the log entry just prior to this one */ public AppendRequest(int clusterId, String senderId, String recipientId, long term, Timestamp leaderTimestamp, Timestamp leaderLeaseTimeout, long leaderCommit, long prevLogTerm, long prevLogIndex) { this(clusterId, senderId, recipientId, term, leaderTimestamp, leaderLeaseTimeout, Math.min(leaderCommit, prevLogIndex), prevLogTerm, prevLogIndex, 0, null); } /** * Constructor for a request that contains an actual log entry. * * @param clusterId cluster ID * @param senderId identity of sender * @param recipientId identity of recipient * @param term sender's current term * @param leaderTimestamp leader's timestamp for this request * @param leaderLeaseTimeout earliest leader timestamp at which leader could be deposed (or null) * @param leaderCommit current commit index for sender * @param prevLogTerm term of the log entry just prior to this one * @param prevLogIndex index of the log entry just prior to this one * @param logEntryTerm term of this log entry * @param mutationData log entry serialized mutations, or null if follower should have the data already */ public AppendRequest(int clusterId, String senderId, String recipientId, long term, Timestamp leaderTimestamp, Timestamp leaderLeaseTimeout, long leaderCommit, long prevLogTerm, long prevLogIndex, long logEntryTerm, ByteBuffer mutationData) { super(Message.APPEND_REQUEST_TYPE, clusterId, senderId, recipientId, term); this.leaderTimestamp = leaderTimestamp; this.leaderLeaseTimeout = leaderLeaseTimeout; this.leaderCommit = leaderCommit; this.prevLogTerm = prevLogTerm; this.prevLogIndex = prevLogIndex; this.logEntryTerm = logEntryTerm; this.mutationData = mutationData; this.checkArguments(); } AppendRequest(ByteBuffer buf, int version) { super(Message.APPEND_REQUEST_TYPE, buf, version); this.leaderTimestamp = Message.getTimestamp(buf, version); this.leaderLeaseTimeout = Message.getBoolean(buf) ? this.leaderTimestamp.offset((int)LongEncoder.read(buf)) : null; this.leaderCommit = LongEncoder.read(buf); this.prevLogTerm = LongEncoder.read(buf); this.prevLogIndex = LongEncoder.read(buf); this.logEntryTerm = LongEncoder.read(buf); this.mutationData = this.logEntryTerm != 0 && Message.getBoolean(buf) ? Message.getByteBuffer(buf) : null; this.checkArguments(); } @Override void checkArguments() { super.checkArguments(); Preconditions.checkArgument(this.leaderTimestamp != null); Preconditions.checkArgument(this.leaderCommit >= 0); Preconditions.checkArgument(this.prevLogTerm >= 0); Preconditions.checkArgument(this.prevLogIndex >= 0); Preconditions.checkArgument(this.logEntryTerm >= 0); Preconditions.checkArgument(this.mutationData == null || this.logEntryTerm > 0); } // Properties public Timestamp getLeaderTimestamp() { return this.leaderTimestamp; } public Timestamp getLeaderLeaseTimeout() { return this.leaderLeaseTimeout; } /** * Get leader's commit index. * *

* For {@linkplain #isProbe probe} requests, this value is limited to the value returned by {@link #getPrevLogIndex}. * * @return leader's commit index */ public long getLeaderCommit() { return this.leaderCommit; } public long getPrevLogTerm() { return this.prevLogTerm; } public long getPrevLogIndex() { return this.prevLogIndex; } public boolean isProbe() { return this.logEntryTerm == 0; } public long getLogEntryTerm() { return this.logEntryTerm; } /** * Get the serialized data for the log entry, if any. * Returns null if this is a probe or follower is expected to already have the data from a transaction. * *

* This method may only be invoked once. * * @return log entry serialized mutations, or null if this message does not contain data * @throws IllegalStateException if this method has already been invoked */ public ByteBuffer getMutationData() { Preconditions.checkState(!this.mutationDataInvalid); final ByteBuffer result = this.mutationData; this.mutationData = null; this.mutationDataInvalid = true; return result; } // Message @Override public boolean isLeaderMessage() { return true; } @Override public void visit(MessageSwitch handler) { handler.caseAppendRequest(this); } @Override public void writeTo(ByteBuffer dest, int version) { Preconditions.checkState(!this.mutationDataInvalid); super.writeTo(dest, version); Message.putTimestamp(dest, this.leaderTimestamp, version); Message.putBoolean(dest, this.leaderLeaseTimeout != null); if (this.leaderLeaseTimeout != null) LongEncoder.write(dest, this.leaderLeaseTimeout.offsetFrom(this.leaderTimestamp)); LongEncoder.write(dest, this.leaderCommit); LongEncoder.write(dest, this.prevLogTerm); LongEncoder.write(dest, this.prevLogIndex); LongEncoder.write(dest, this.logEntryTerm); if (this.logEntryTerm != 0) { Message.putBoolean(dest, this.mutationData != null); if (this.mutationData != null) Message.putByteBuffer(dest, this.mutationData); } } @Override protected int calculateSize(int version) { Preconditions.checkState(!this.mutationDataInvalid); return super.calculateSize(version) + Message.calculateSize(this.leaderTimestamp, version) + 1 + (this.leaderLeaseTimeout != null ? LongEncoder.encodeLength(this.leaderLeaseTimeout.offsetFrom(this.leaderTimestamp)) : 0) + LongEncoder.encodeLength(this.leaderCommit) + LongEncoder.encodeLength(this.prevLogTerm) + LongEncoder.encodeLength(this.prevLogIndex) + LongEncoder.encodeLength(this.logEntryTerm) + (this.logEntryTerm != 0 ? 1 + (this.mutationData != null ? Message.calculateSize(this.mutationData) : 0) : 0); } // Object @Override public String toString() { return this.getClass().getSimpleName() + "[\"" + this.getSenderId() + "\"->\"" + this.getRecipientId() + "\"" + ",clusterId=" + String.format("%08x", this.getClusterId()) + ",term=" + this.getTerm() + ",leaderTimestamp=" + this.leaderTimestamp + (this.leaderLeaseTimeout != null ? ",leaderLeaseTimeout=" + String.format("%+dms", this.leaderLeaseTimeout.offsetFrom(this.leaderTimestamp)) : "") + ",leaderCommit=" + this.leaderCommit + ",prevLog=" + this.prevLogIndex + "t" + this.prevLogTerm + (this.logEntryTerm != 0 ? ",logEntryTerm=" + this.logEntryTerm : "") + (this.mutationData != null ? ",mutationData=" + this.describe(this.mutationData) : this.mutationDataInvalid ? ",mutationData=invalid" : "") + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy